package envoy.client.data; import java.io.*; import java.util.*; import javafx.beans.property.*; import javafx.collections.*; import envoy.data.*; import envoy.data.Message.MessageStatus; import envoy.event.MessageStatusChange; import envoy.client.net.WriteProxy; /** * Represents a chat between two {@link User}s as a list of {@link Message} objects. * * @author Maximilian Käfer * @author Leon Hofmeister * @author Kai S. K. Engelbart * @since Envoy Client v0.1-alpha */ public class Chat implements Serializable { protected transient ObservableList messages = FXCollections.observableArrayList(); protected final Contact recipient; protected boolean disabled; protected boolean underlyingContactDeleted; /** * Stores the last time an {@link envoy.event.IsTyping} event has been sent. */ protected transient long lastWritingEvent; protected int unreadAmount; protected static IntegerProperty totalUnreadAmount = new SimpleIntegerProperty(); private static final long serialVersionUID = 2L; /** * Provides the list of messages that the recipient receives. *

* Saves the Messages in the corresponding chat at that Point. * * @param recipient the user who receives the messages * @since Envoy Client v0.1-alpha */ public Chat(Contact recipient) { this.recipient = recipient; } private void readObject(ObjectInputStream stream) throws ClassNotFoundException, IOException { stream.defaultReadObject(); messages = FXCollections.observableList((List) stream.readObject()); totalUnreadAmount.set(totalUnreadAmount.get() + unreadAmount); } private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); stream.writeObject(new ArrayList<>(messages)); } @Override public String toString() { return String.format( "%s[recipient=%s,messages=%d,disabled=%b]", getClass().getSimpleName(), recipient, messages.size(), disabled); } /** * Generates a hash code based on the recipient. * * @since Envoy Client v0.1-beta */ @Override public int hashCode() { return Objects.hash(recipient); } /** * 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; final var other = (Chat) obj; return Objects.equals(recipient, other.recipient); } /** * Sets the status of all chat messages received from the recipient to {@code READ} starting * from the bottom and stopping once a read message is found. * * @param writeProxy the write proxy instance used to notify the server about the message status * changes * @since Envoy Client v0.3-alpha */ public void read(WriteProxy writeProxy) { for (int i = messages.size() - 1; i >= 0; --i) { final var m = messages.get(i); if (m.getSenderID() == recipient.getID()) if (m.getStatus() == MessageStatus.READ) break; else { m.setStatus(MessageStatus.READ); writeProxy.writeMessageStatusChange(new MessageStatusChange(m)); } } totalUnreadAmount.set(totalUnreadAmount.get() - unreadAmount); unreadAmount = 0; } /** * @return {@code true} if the newest message received in the chat doesn't have the status * {@code READ} * @since Envoy Client v0.3-alpha */ public boolean isUnread() { return !messages.isEmpty() && messages.get(messages.size() - 1).getStatus() != MessageStatus.READ; } /** * Inserts a message at the correct place according to its creation date. * * @param message the message to insert * @since Envoy Client v0.1-beta */ public void insert(Message message) { for (int i = messages.size() - 1; i >= 0; --i) if (message.getCreationDate().isAfter(messages.get(i).getCreationDate())) { messages.add(i + 1, message); return; } messages.add(0, message); } /** * Removes the message with the given ID. * * @param messageID the ID of the message to remove * @return whether the message has been found and removed * @since Envoy Client v0.3-beta */ public boolean remove(long messageID) { return messages.removeIf(m -> m.getID() == messageID); } /** * @return an integer property storing the total amount of unread messages * @since Envoy Client v0.3-beta */ public static IntegerProperty getTotalUnreadAmount() { return totalUnreadAmount; } /** * Increments the amount of unread messages. * * @since Envoy Client v0.1-beta */ public void incrementUnreadAmount() { ++unreadAmount; totalUnreadAmount.set(totalUnreadAmount.get() + 1); } /** * @return the amount of unread messages in this chat * @since Envoy Client v0.1-beta */ public int getUnreadAmount() { return unreadAmount; } /** * @return all messages in the current chat * @since Envoy Client v0.1-beta */ public ObservableList getMessages() { return messages; } /** * @return the recipient of a message * @since Envoy Client v0.1-alpha */ public Contact getRecipient() { return recipient; } /** * @return the last known time a {@link envoy.event.IsTyping} 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(); } /** * Determines whether messages can be sent in this chat. Should be {@code true} i.e. for chats * whose recipient deleted this client as a contact. * * @return whether this chat has been disabled * @since Envoy Client v0.3-beta */ public boolean isDisabled() { return disabled; } /** * Determines whether messages can be sent in this chat. Should be true i.e. for chats whose * recipient deleted this client as a contact. * * @param disabled whether this chat should be disabled * @since Envoy Client v0.3-beta */ public void setDisabled(boolean disabled) { this.disabled = disabled; } }