Merge pull request #174 from informatik-ag-ngl/f/groupMessages
Group Messages
This commit is contained in:
commit
3e3d651b87
@ -24,10 +24,10 @@ import envoy.event.MessageStatusChange;
|
|||||||
* @author Kai S. K. Engelbart
|
* @author Kai S. K. Engelbart
|
||||||
* @since Envoy Client v0.1-alpha
|
* @since Envoy Client v0.1-alpha
|
||||||
*/
|
*/
|
||||||
public final class Chat implements Serializable {
|
public class Chat implements Serializable {
|
||||||
|
|
||||||
private final Contact recipient;
|
protected final Contact recipient;
|
||||||
private final List<Message> messages = new ArrayList<>();
|
protected final List<Message> messages = new ArrayList<>();
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@ -39,7 +39,9 @@ public final class Chat implements Serializable {
|
|||||||
* @param recipient the user who receives the messages
|
* @param recipient the user who receives the messages
|
||||||
* @since Envoy Client v0.1-alpha
|
* @since Envoy Client v0.1-alpha
|
||||||
*/
|
*/
|
||||||
public Chat(Contact recipient) { this.recipient = recipient; }
|
public Chat(Contact recipient) {
|
||||||
|
this.recipient = recipient;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() { return String.format("Chat[recipient=%s,messages=%d]", recipient, messages.size()); }
|
public String toString() { return String.format("Chat[recipient=%s,messages=%d]", recipient, messages.size()); }
|
||||||
|
54
src/main/java/envoy/client/data/GroupChat.java
Normal file
54
src/main/java/envoy/client/data/GroupChat.java
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package envoy.client.data;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
import envoy.client.net.WriteProxy;
|
||||||
|
import envoy.data.Contact;
|
||||||
|
import envoy.data.GroupMessage;
|
||||||
|
import envoy.data.Message.MessageStatus;
|
||||||
|
import envoy.data.User;
|
||||||
|
import envoy.event.GroupMessageStatusChange;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a chat between a user and a group
|
||||||
|
* as a list of messages.
|
||||||
|
* <p>
|
||||||
|
* Project: <strong>envoy-client</strong><br>
|
||||||
|
* File: <strong>GroupChat.java</strong><br>
|
||||||
|
* Created: <strong>05.07.2020</strong><br>
|
||||||
|
*
|
||||||
|
* @author Maximilian Käfer
|
||||||
|
* @since Envoy Client v0.1-beta
|
||||||
|
*/
|
||||||
|
public class GroupChat extends Chat {
|
||||||
|
|
||||||
|
private final User sender;
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param sender the user sending the messages
|
||||||
|
* @param recipient the group whose members receive the messages
|
||||||
|
* @since Envoy Client v0.1-beta
|
||||||
|
*/
|
||||||
|
public GroupChat(User sender, Contact recipient) {
|
||||||
|
super(recipient);
|
||||||
|
this.sender = sender;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(WriteProxy writeProxy) throws IOException {
|
||||||
|
for (int i = messages.size() - 1; i >= 0; --i) {
|
||||||
|
final GroupMessage gmsg = (GroupMessage) messages.get(i);
|
||||||
|
if (gmsg.getSenderID() != sender.getID()) {
|
||||||
|
if (gmsg.getMemberStatuses().get(sender.getID()) == MessageStatus.READ) break;
|
||||||
|
else {
|
||||||
|
gmsg.getMemberStatuses().replace(sender.getID(), MessageStatus.READ);
|
||||||
|
writeProxy
|
||||||
|
.writeMessageStatusChange(new GroupMessageStatusChange(gmsg.getID(), MessageStatus.READ, LocalDateTime.now(), sender.getID()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -82,7 +82,11 @@ public abstract class LocalDB {
|
|||||||
getChat(contact.getID()).ifPresent(chat -> { ((User) chat.getRecipient()).setStatus(((User) contact).getStatus()); });
|
getChat(contact.getID()).ifPresent(chat -> { ((User) chat.getRecipient()).setStatus(((User) contact).getStatus()); });
|
||||||
|
|
||||||
// Create missing chats
|
// Create missing chats
|
||||||
user.getContacts().stream().filter(u -> !u.equals(user) && getChat(u.getID()).isEmpty()).map(Chat::new).forEach(chats::add);
|
user.getContacts()
|
||||||
|
.stream()
|
||||||
|
.filter(c -> !c.equals(user) && getChat(c.getID()).isEmpty())
|
||||||
|
.map(c -> c instanceof User ? new Chat(c) : new GroupChat(user, c))
|
||||||
|
.forEach(chats::add);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -53,23 +53,34 @@ public class Client implements Closeable {
|
|||||||
* will block for up to 5 seconds. If the handshake does exceed this time limit,
|
* will block for up to 5 seconds. If the handshake does exceed this time limit,
|
||||||
* an exception is thrown.
|
* an exception is thrown.
|
||||||
*
|
*
|
||||||
* @param credentials the login credentials of the user
|
* @param credentials the login credentials of the
|
||||||
* @param receivedMessageCache a message cache containing all unread
|
* user
|
||||||
* messages
|
* @param receivedMessageCache a message cache containing all
|
||||||
* from the server that can be relayed
|
* unread messages from the server
|
||||||
* after
|
* that can be relayed after
|
||||||
* initialization
|
* initialization
|
||||||
* @param receivedMessageStatusChangeCache an event cache containing all
|
* @param receivedGroupMessageCache a groupMessage cache containing
|
||||||
* received messageStatusChangeEvents
|
* all unread groupMessages from
|
||||||
* from the server that can be relayed
|
* the server that can be relayed
|
||||||
* after initialization
|
* after initialization
|
||||||
|
* @param receivedMessageStatusChangeCache an event cache containing all
|
||||||
|
* received
|
||||||
|
* messageStatusChangeEvents from
|
||||||
|
* the server that can be relayed
|
||||||
|
* after initialization
|
||||||
|
* @param receivedGroupMessageStatusChangeCache an event cache containing all
|
||||||
|
* received
|
||||||
|
* groupMessageStatusChangeEvents
|
||||||
|
* from the server that can be
|
||||||
|
* relayed after initialization
|
||||||
* @throws TimeoutException if the server could not be reached
|
* @throws TimeoutException if the server could not be reached
|
||||||
* @throws IOException if the login credentials could not be written
|
* @throws IOException if the login credentials could not be written
|
||||||
* @throws InterruptedException if the current thread is interrupted while
|
* @throws InterruptedException if the current thread is interrupted while
|
||||||
* waiting for the handshake response
|
* waiting for the handshake response
|
||||||
*/
|
*/
|
||||||
public void performHandshake(LoginCredentials credentials, Cache<Message> receivedMessageCache,
|
public void performHandshake(LoginCredentials credentials, Cache<Message> receivedMessageCache, Cache<GroupMessage> receivedGroupMessageCache,
|
||||||
Cache<MessageStatusChange> receivedMessageStatusChangeCache) throws TimeoutException, IOException, InterruptedException {
|
Cache<MessageStatusChange> receivedMessageStatusChangeCache, Cache<GroupMessageStatusChange> receivedGroupMessageStatusChangeCache)
|
||||||
|
throws TimeoutException, IOException, InterruptedException {
|
||||||
if (online) throw new IllegalStateException("Handshake has already been performed successfully");
|
if (online) throw new IllegalStateException("Handshake has already been performed successfully");
|
||||||
|
|
||||||
// Establish TCP connection
|
// Establish TCP connection
|
||||||
@ -83,7 +94,9 @@ public class Client implements Closeable {
|
|||||||
// Register user creation processor, contact list processor and message cache
|
// Register user creation processor, contact list processor and message cache
|
||||||
receiver.registerProcessor(User.class, sender -> this.sender = sender);
|
receiver.registerProcessor(User.class, sender -> this.sender = sender);
|
||||||
receiver.registerProcessor(Message.class, receivedMessageCache);
|
receiver.registerProcessor(Message.class, receivedMessageCache);
|
||||||
|
receiver.registerProcessor(GroupMessage.class, receivedGroupMessageCache);
|
||||||
receiver.registerProcessor(MessageStatusChange.class, receivedMessageStatusChangeCache);
|
receiver.registerProcessor(MessageStatusChange.class, receivedMessageStatusChangeCache);
|
||||||
|
receiver.registerProcessor(GroupMessageStatusChange.class, receivedGroupMessageStatusChangeCache);
|
||||||
receiver.registerProcessor(HandshakeRejection.class, evt -> { rejected = true; eventBus.dispatch(evt); });
|
receiver.registerProcessor(HandshakeRejection.class, evt -> { rejected = true; eventBus.dispatch(evt); });
|
||||||
|
|
||||||
rejected = false;
|
rejected = false;
|
||||||
@ -122,38 +135,65 @@ public class Client implements Closeable {
|
|||||||
* Initializes the {@link Receiver} used to process data sent from the server to
|
* Initializes the {@link Receiver} used to process data sent from the server to
|
||||||
* this client.
|
* this client.
|
||||||
*
|
*
|
||||||
* @param localDB the local database used to persist
|
* @param localDB the local database used to
|
||||||
* the current
|
* persist
|
||||||
* {@link IDGenerator}
|
* the current
|
||||||
* @param receivedMessageCache a message cache containing all unread
|
* {@link IDGenerator}
|
||||||
* messages
|
* @param receivedMessageCache a message cache containing all
|
||||||
* from the server that can be relayed
|
* unread
|
||||||
* after
|
* messages
|
||||||
* initialization
|
* from the server that can be
|
||||||
* @param receivedMessageStatusChangeCache an event cache containing all
|
* relayed
|
||||||
* received messageStatusChangeEvents
|
* after
|
||||||
* from the server that can be relayed
|
* initialization
|
||||||
* after initialization
|
* @param receivedGroupMessageCache a groupMessage cache containing
|
||||||
|
* all
|
||||||
|
* unread
|
||||||
|
* groupMessages
|
||||||
|
* from the server that can be
|
||||||
|
* relayed
|
||||||
|
* after
|
||||||
|
* initialization
|
||||||
|
* @param receivedMessageStatusChangeCache an event cache containing all
|
||||||
|
* received
|
||||||
|
* messageStatusChangeEvents
|
||||||
|
* from the server that can be
|
||||||
|
* relayed
|
||||||
|
* after initialization
|
||||||
|
* @param receivedGroupMessageStatusChangeCache an event cache containing all
|
||||||
|
* received
|
||||||
|
* groupMessageStatusChangeEvents
|
||||||
|
* from the server that can be
|
||||||
|
* relayed after initialization
|
||||||
* @throws IOException if no {@link IDGenerator} is present and none could be
|
* @throws IOException if no {@link IDGenerator} is present and none could be
|
||||||
* requested from the server
|
* requested from the server
|
||||||
* @since Envoy Client v0.2-alpha
|
* @since Envoy Client v0.2-alpha
|
||||||
*/
|
*/
|
||||||
public void initReceiver(LocalDB localDB, Cache<Message> receivedMessageCache, Cache<MessageStatusChange> receivedMessageStatusChangeCache)
|
public void initReceiver(LocalDB localDB, Cache<Message> receivedMessageCache, Cache<GroupMessage> receivedGroupMessageCache,
|
||||||
|
Cache<MessageStatusChange> receivedMessageStatusChangeCache, Cache<GroupMessageStatusChange> receivedGroupMessageStatusChangeCache)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
checkOnline();
|
checkOnline();
|
||||||
|
|
||||||
// Process incoming messages
|
// Process incoming messages
|
||||||
final ReceivedMessageProcessor receivedMessageProcessor = new ReceivedMessageProcessor();
|
final ReceivedMessageProcessor receivedMessageProcessor = new ReceivedMessageProcessor();
|
||||||
final MessageStatusChangeProcessor messageStatusChangeEventProcessor = new MessageStatusChangeProcessor();
|
final ReceivedGroupMessageProcessor receivedGroupMessageProcessor = new ReceivedGroupMessageProcessor();
|
||||||
|
final MessageStatusChangeProcessor messageStatusChangeProcessor = new MessageStatusChangeProcessor();
|
||||||
|
final GroupMessageStatusChangeProcessor groupMessageStatusChangeProcessor = new GroupMessageStatusChangeProcessor();
|
||||||
|
|
||||||
|
receiver.registerProcessor(GroupMessage.class, receivedGroupMessageProcessor);
|
||||||
|
|
||||||
receiver.registerProcessor(Message.class, receivedMessageProcessor);
|
receiver.registerProcessor(Message.class, receivedMessageProcessor);
|
||||||
|
|
||||||
// Relay cached unread messages
|
receiver.registerProcessor(MessageStatusChange.class, messageStatusChangeProcessor);
|
||||||
|
|
||||||
|
receiver.registerProcessor(GroupMessageStatusChange.class, groupMessageStatusChangeProcessor);
|
||||||
|
// Relay cached unread messages and unread groupMessages
|
||||||
receivedMessageCache.setProcessor(receivedMessageProcessor);
|
receivedMessageCache.setProcessor(receivedMessageProcessor);
|
||||||
|
receivedGroupMessageCache.setProcessor(receivedGroupMessageProcessor);
|
||||||
|
|
||||||
// Process message status changes
|
// Process message status changes
|
||||||
receiver.registerProcessor(MessageStatusChange.class, messageStatusChangeEventProcessor);
|
receivedMessageStatusChangeCache.setProcessor(messageStatusChangeProcessor);
|
||||||
receivedMessageStatusChangeCache.setProcessor(messageStatusChangeEventProcessor);
|
receivedGroupMessageStatusChangeCache.setProcessor(groupMessageStatusChangeProcessor);
|
||||||
|
|
||||||
// Process user status changes
|
// Process user status changes
|
||||||
receiver.registerProcessor(UserStatusChange.class, eventBus::dispatch);
|
receiver.registerProcessor(UserStatusChange.class, eventBus::dispatch);
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
package envoy.client.net;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import envoy.data.Message.MessageStatus;
|
||||||
|
import envoy.event.EventBus;
|
||||||
|
import envoy.event.GroupMessageStatusChange;
|
||||||
|
import envoy.util.EnvoyLog;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Project: <strong>envoy-client</strong><br>
|
||||||
|
* File: <strong>GroupMessageStatusChangePocessor.java</strong><br>
|
||||||
|
* Created: <strong>03.07.2020</strong><br>
|
||||||
|
*
|
||||||
|
* @author Maximilian Käfer
|
||||||
|
* @since Envoy Client v0.1-beta
|
||||||
|
*/
|
||||||
|
public class GroupMessageStatusChangeProcessor implements Consumer<GroupMessageStatusChange> {
|
||||||
|
|
||||||
|
private static final Logger logger = EnvoyLog.getLogger(GroupMessageStatusChangeProcessor.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(GroupMessageStatusChange evt) {
|
||||||
|
if (evt.get().ordinal() < MessageStatus.RECEIVED.ordinal()) logger.warning("Received invalid group message status change " + evt);
|
||||||
|
else EventBus.getInstance().dispatch(evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package envoy.client.net;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import envoy.client.event.MessageCreationEvent;
|
||||||
|
import envoy.data.GroupMessage;
|
||||||
|
import envoy.data.Message.MessageStatus;
|
||||||
|
import envoy.event.EventBus;
|
||||||
|
import envoy.util.EnvoyLog;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Project: <strong>envoy-client</strong><br>
|
||||||
|
* File: <strong>ReceivedGroupMessageProcessor.java</strong><br>
|
||||||
|
* Created: <strong>13.06.2020</strong><br>
|
||||||
|
*
|
||||||
|
* @author Maximilian Käfer
|
||||||
|
* @since Envoy Client v0.1-beta
|
||||||
|
*/
|
||||||
|
public class ReceivedGroupMessageProcessor implements Consumer<GroupMessage> {
|
||||||
|
|
||||||
|
private static final Logger logger = EnvoyLog.getLogger(ReceivedGroupMessageProcessor.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(GroupMessage groupMessage) {
|
||||||
|
if (groupMessage.getStatus() == MessageStatus.WAITING || groupMessage.getStatus() == MessageStatus.READ)
|
||||||
|
logger.warning("The groupMessage has the unexpected status " + groupMessage.getStatus());
|
||||||
|
|
||||||
|
// Dispatch event
|
||||||
|
EventBus.getInstance().dispatch(new MessageCreationEvent(groupMessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -78,8 +78,10 @@ public class Receiver extends Thread {
|
|||||||
// Get appropriate processor
|
// Get appropriate processor
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
final Consumer processor = processors.get(obj.getClass());
|
final Consumer processor = processors.get(obj.getClass());
|
||||||
if (processor == null) logger.log(Level.WARNING,
|
if (processor == null)
|
||||||
String.format("The received object has the class %s for which no processor is defined.", obj.getClass()));
|
logger.log(Level.WARNING, String.format(
|
||||||
|
"The received object has the %s for which no processor is defined.",
|
||||||
|
obj.getClass()));
|
||||||
else processor.accept(obj);
|
else processor.accept(obj);
|
||||||
}
|
}
|
||||||
} catch (final SocketException e) {
|
} catch (final SocketException e) {
|
||||||
|
@ -15,7 +15,9 @@ import envoy.client.data.*;
|
|||||||
import envoy.client.net.Client;
|
import envoy.client.net.Client;
|
||||||
import envoy.client.ui.SceneContext.SceneInfo;
|
import envoy.client.ui.SceneContext.SceneInfo;
|
||||||
import envoy.client.ui.controller.LoginScene;
|
import envoy.client.ui.controller.LoginScene;
|
||||||
|
import envoy.data.GroupMessage;
|
||||||
import envoy.data.Message;
|
import envoy.data.Message;
|
||||||
|
import envoy.event.GroupMessageStatusChange;
|
||||||
import envoy.event.MessageStatusChange;
|
import envoy.event.MessageStatusChange;
|
||||||
import envoy.exception.EnvoyException;
|
import envoy.exception.EnvoyException;
|
||||||
import envoy.util.EnvoyLog;
|
import envoy.util.EnvoyLog;
|
||||||
@ -43,7 +45,9 @@ public final class Startup extends Application {
|
|||||||
private LocalDB localDB;
|
private LocalDB localDB;
|
||||||
private Client client;
|
private Client client;
|
||||||
private Cache<Message> messageCache;
|
private Cache<Message> messageCache;
|
||||||
|
private Cache<GroupMessage> groupMessageCache;
|
||||||
private Cache<MessageStatusChange> messageStatusCache;
|
private Cache<MessageStatusChange> messageStatusCache;
|
||||||
|
private Cache<GroupMessageStatusChange> groupMessageStatusCache;
|
||||||
|
|
||||||
private static final ClientConfig config = ClientConfig.getInstance();
|
private static final ClientConfig config = ClientConfig.getInstance();
|
||||||
private static final Logger logger = EnvoyLog.getLogger(Startup.class);
|
private static final Logger logger = EnvoyLog.getLogger(Startup.class);
|
||||||
@ -99,14 +103,17 @@ public final class Startup extends Application {
|
|||||||
// Initialize client and unread message cache
|
// Initialize client and unread message cache
|
||||||
client = new Client();
|
client = new Client();
|
||||||
messageCache = new Cache<>();
|
messageCache = new Cache<>();
|
||||||
|
groupMessageCache = new Cache<>();
|
||||||
messageStatusCache = new Cache<>();
|
messageStatusCache = new Cache<>();
|
||||||
|
groupMessageStatusCache = new Cache<>();
|
||||||
|
|
||||||
stage.setTitle("Envoy");
|
stage.setTitle("Envoy");
|
||||||
stage.getIcons().add(IconUtil.loadIcon("envoy_logo"));
|
stage.getIcons().add(IconUtil.loadIcon("envoy_logo"));
|
||||||
|
|
||||||
final var sceneContext = new SceneContext(stage);
|
final var sceneContext = new SceneContext(stage);
|
||||||
sceneContext.load(SceneInfo.LOGIN_SCENE);
|
sceneContext.load(SceneInfo.LOGIN_SCENE);
|
||||||
sceneContext.<LoginScene>getController().initializeData(client, localDB, messageCache, messageStatusCache, sceneContext);
|
sceneContext.<LoginScene>getController()
|
||||||
|
.initializeData(client, localDB, messageCache, groupMessageCache, messageStatusCache, groupMessageStatusCache, sceneContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -17,9 +17,7 @@ import javafx.scene.input.KeyCode;
|
|||||||
import javafx.scene.input.KeyEvent;
|
import javafx.scene.input.KeyEvent;
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
|
|
||||||
import envoy.client.data.Chat;
|
import envoy.client.data.*;
|
||||||
import envoy.client.data.LocalDB;
|
|
||||||
import envoy.client.data.Settings;
|
|
||||||
import envoy.client.data.audio.AudioRecorder;
|
import envoy.client.data.audio.AudioRecorder;
|
||||||
import envoy.client.event.MessageCreationEvent;
|
import envoy.client.event.MessageCreationEvent;
|
||||||
import envoy.client.net.Client;
|
import envoy.client.net.Client;
|
||||||
@ -32,9 +30,7 @@ import envoy.client.ui.listcell.MessageControl;
|
|||||||
import envoy.client.ui.listcell.MessageListCellFactory;
|
import envoy.client.ui.listcell.MessageListCellFactory;
|
||||||
import envoy.data.*;
|
import envoy.data.*;
|
||||||
import envoy.data.Attachment.AttachmentType;
|
import envoy.data.Attachment.AttachmentType;
|
||||||
import envoy.event.EventBus;
|
import envoy.event.*;
|
||||||
import envoy.event.MessageStatusChange;
|
|
||||||
import envoy.event.UserStatusChange;
|
|
||||||
import envoy.event.contact.ContactOperation;
|
import envoy.event.contact.ContactOperation;
|
||||||
import envoy.exception.EnvoyException;
|
import envoy.exception.EnvoyException;
|
||||||
import envoy.util.EnvoyLog;
|
import envoy.util.EnvoyLog;
|
||||||
@ -116,9 +112,8 @@ public final class ChatScene implements Restorable {
|
|||||||
// Listen to received messages
|
// Listen to received messages
|
||||||
eventBus.register(MessageCreationEvent.class, e -> {
|
eventBus.register(MessageCreationEvent.class, e -> {
|
||||||
final var message = e.get();
|
final var message = e.get();
|
||||||
localDB.getChat(message.getSenderID()).ifPresent(chat -> {
|
localDB.getChat(message instanceof GroupMessage ? message.getRecipientID() : message.getSenderID()).ifPresent(chat -> {
|
||||||
chat.insert(message);
|
chat.insert(message);
|
||||||
|
|
||||||
if (chat.equals(currentChat)) {
|
if (chat.equals(currentChat)) {
|
||||||
try {
|
try {
|
||||||
currentChat.read(writeProxy);
|
currentChat.read(writeProxy);
|
||||||
@ -133,11 +128,17 @@ public final class ChatScene implements Restorable {
|
|||||||
// Listen to message status changes
|
// Listen to message status changes
|
||||||
eventBus.register(MessageStatusChange.class, e -> localDB.getMessage(e.getID()).ifPresent(message -> {
|
eventBus.register(MessageStatusChange.class, e -> localDB.getMessage(e.getID()).ifPresent(message -> {
|
||||||
message.setStatus(e.get());
|
message.setStatus(e.get());
|
||||||
|
|
||||||
// Update UI if in current chat
|
// Update UI if in current chat
|
||||||
if (currentChat != null && message.getSenderID() == currentChat.getRecipient().getID()) Platform.runLater(messageList::refresh);
|
if (currentChat != null && message.getSenderID() == currentChat.getRecipient().getID()) Platform.runLater(messageList::refresh);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
eventBus.register(GroupMessageStatusChange.class, e -> localDB.getMessage(e.getID()).ifPresent(groupMessage -> {
|
||||||
|
((GroupMessage) groupMessage).getMemberStatuses().replace(e.getMemberID(), e.get());
|
||||||
|
|
||||||
|
// Update UI if in current chat
|
||||||
|
if (currentChat != null && groupMessage.getRecipientID() == currentChat.getRecipient().getID()) Platform.runLater(messageList::refresh);
|
||||||
|
}));
|
||||||
|
|
||||||
// Listen to user status changes
|
// Listen to user status changes
|
||||||
eventBus.register(UserStatusChange.class,
|
eventBus.register(UserStatusChange.class,
|
||||||
e -> userList.getItems()
|
e -> userList.getItems()
|
||||||
@ -152,7 +153,7 @@ public final class ChatScene implements Restorable {
|
|||||||
switch (e.getOperationType()) {
|
switch (e.getOperationType()) {
|
||||||
case ADD:
|
case ADD:
|
||||||
localDB.getUsers().put(contact.getName(), contact);
|
localDB.getUsers().put(contact.getName(), contact);
|
||||||
localDB.getChats().add(new Chat(contact));
|
localDB.getChats().add(contact instanceof User ? new Chat(contact) : new GroupChat(client.getSender(), contact));
|
||||||
Platform.runLater(() -> userList.getItems().add(contact));
|
Platform.runLater(() -> userList.getItems().add(contact));
|
||||||
break;
|
break;
|
||||||
case REMOVE:
|
case REMOVE:
|
||||||
@ -354,8 +355,8 @@ public final class ChatScene implements Restorable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a new message to the server based on the text entered in the
|
* Sends a new {@link Message} or {@link GroupMessage} to the server based on
|
||||||
* messageTextArea.
|
* the text entered in the {@code messageTextArea} and the given attachment.
|
||||||
*
|
*
|
||||||
* @since Envoy Client v0.1-beta
|
* @since Envoy Client v0.1-beta
|
||||||
*/
|
*/
|
||||||
@ -371,15 +372,18 @@ public final class ChatScene implements Restorable {
|
|||||||
}
|
}
|
||||||
final var text = messageTextArea.getText().strip();
|
final var text = messageTextArea.getText().strip();
|
||||||
try {
|
try {
|
||||||
// Create and send message
|
// Creating the message and its metadata
|
||||||
final var builder = new MessageBuilder(localDB.getUser().getID(), currentChat.getRecipient().getID(), localDB.getIDGenerator())
|
final var builder = new MessageBuilder(localDB.getUser().getID(), currentChat.getRecipient().getID(), localDB.getIDGenerator())
|
||||||
.setText(text);
|
.setText(text);
|
||||||
if (pendingAttachment != null) {
|
// Setting an attachment, if present
|
||||||
|
if (pendingAttachment != null) {
|
||||||
builder.setAttachment(pendingAttachment);
|
builder.setAttachment(pendingAttachment);
|
||||||
pendingAttachment = null;
|
pendingAttachment = null;
|
||||||
attachmentView.setVisible(false);
|
attachmentView.setVisible(false);
|
||||||
}
|
}
|
||||||
final var message = builder.build();
|
// Building the final message
|
||||||
|
final var message = currentChat.getRecipient() instanceof Group ? builder.buildGroupMessage((Group) currentChat.getRecipient())
|
||||||
|
: builder.build();
|
||||||
|
|
||||||
// Send message
|
// Send message
|
||||||
writeProxy.writeMessage(message);
|
writeProxy.writeMessage(message);
|
||||||
|
@ -16,13 +16,9 @@ import envoy.client.net.Client;
|
|||||||
import envoy.client.ui.ClearableTextField;
|
import envoy.client.ui.ClearableTextField;
|
||||||
import envoy.client.ui.SceneContext;
|
import envoy.client.ui.SceneContext;
|
||||||
import envoy.client.ui.Startup;
|
import envoy.client.ui.Startup;
|
||||||
import envoy.data.LoginCredentials;
|
import envoy.data.*;
|
||||||
import envoy.data.Message;
|
|
||||||
import envoy.data.User;
|
|
||||||
import envoy.data.User.UserStatus;
|
import envoy.data.User.UserStatus;
|
||||||
import envoy.event.EventBus;
|
import envoy.event.*;
|
||||||
import envoy.event.HandshakeRejection;
|
|
||||||
import envoy.event.MessageStatusChange;
|
|
||||||
import envoy.exception.EnvoyException;
|
import envoy.exception.EnvoyException;
|
||||||
import envoy.util.Bounds;
|
import envoy.util.Bounds;
|
||||||
import envoy.util.EnvoyLog;
|
import envoy.util.EnvoyLog;
|
||||||
@ -59,7 +55,9 @@ public final class LoginScene {
|
|||||||
private Client client;
|
private Client client;
|
||||||
private LocalDB localDB;
|
private LocalDB localDB;
|
||||||
private Cache<Message> receivedMessageCache;
|
private Cache<Message> receivedMessageCache;
|
||||||
|
private Cache<GroupMessage> receivedGroupMessageCache;
|
||||||
private Cache<MessageStatusChange> receivedMessageStatusChangeCache;
|
private Cache<MessageStatusChange> receivedMessageStatusChangeCache;
|
||||||
|
private Cache<GroupMessageStatusChange> receivedGroupMessageStatusChangeCache;
|
||||||
private SceneContext sceneContext;
|
private SceneContext sceneContext;
|
||||||
|
|
||||||
private static final Logger logger = EnvoyLog.getLogger(LoginScene.class);
|
private static final Logger logger = EnvoyLog.getLogger(LoginScene.class);
|
||||||
@ -77,27 +75,40 @@ public final class LoginScene {
|
|||||||
/**
|
/**
|
||||||
* Loads the login dialog using the FXML file {@code LoginDialog.fxml}.
|
* Loads the login dialog using the FXML file {@code LoginDialog.fxml}.
|
||||||
*
|
*
|
||||||
* @param client the client used to perform the
|
* @param client the client used to perform the
|
||||||
* handshake
|
* handshake
|
||||||
* @param localDB the local database used for offline
|
* @param localDB the local database used for
|
||||||
* login
|
* offline
|
||||||
* @param receivedMessageCache the cache storing messages received
|
* login
|
||||||
* during
|
* @param receivedMessageCache the cache storing messages
|
||||||
* the handshake
|
* received
|
||||||
* @param receivedMessageStatusChangeCache the cache storing
|
* during
|
||||||
* messageStatusChangeEvents received
|
* the handshake
|
||||||
* during handshake
|
* @param receivedGroupMessageCache the cache storing groupMessages
|
||||||
* @param sceneContext the scene context used to initialize
|
* received during the handshake
|
||||||
* the chat
|
* @param receivedMessageStatusChangeCache the cache storing
|
||||||
* scene
|
* messageStatusChangeEvents
|
||||||
|
* received
|
||||||
|
* during handshake
|
||||||
|
* @param receivedGroupMessageStatusChangeCache the cache storing
|
||||||
|
* groupMessageStatusChangeEvents
|
||||||
|
* received
|
||||||
|
* during handshake
|
||||||
|
* @param sceneContext the scene context used to
|
||||||
|
* initialize
|
||||||
|
* the chat
|
||||||
|
* scene
|
||||||
* @since Envoy Client v0.1-beta
|
* @since Envoy Client v0.1-beta
|
||||||
*/
|
*/
|
||||||
public void initializeData(Client client, LocalDB localDB, Cache<Message> receivedMessageCache,
|
public void initializeData(Client client, LocalDB localDB, Cache<Message> receivedMessageCache, Cache<GroupMessage> receivedGroupMessageCache,
|
||||||
Cache<MessageStatusChange> receivedMessageStatusChangeCache, SceneContext sceneContext) {
|
Cache<MessageStatusChange> receivedMessageStatusChangeCache, Cache<GroupMessageStatusChange> receivedGroupMessageStatusChangeCache,
|
||||||
|
SceneContext sceneContext) {
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.localDB = localDB;
|
this.localDB = localDB;
|
||||||
this.receivedMessageCache = receivedMessageCache;
|
this.receivedMessageCache = receivedMessageCache;
|
||||||
|
this.receivedGroupMessageCache = receivedGroupMessageCache;
|
||||||
this.receivedMessageStatusChangeCache = receivedMessageStatusChangeCache;
|
this.receivedMessageStatusChangeCache = receivedMessageStatusChangeCache;
|
||||||
|
this.receivedGroupMessageStatusChangeCache = receivedGroupMessageStatusChangeCache;
|
||||||
this.sceneContext = sceneContext;
|
this.sceneContext = sceneContext;
|
||||||
|
|
||||||
// Prepare handshake
|
// Prepare handshake
|
||||||
@ -146,9 +157,17 @@ public final class LoginScene {
|
|||||||
|
|
||||||
private void performHandshake(LoginCredentials credentials) {
|
private void performHandshake(LoginCredentials credentials) {
|
||||||
try {
|
try {
|
||||||
client.performHandshake(credentials, receivedMessageCache, receivedMessageStatusChangeCache);
|
client.performHandshake(credentials,
|
||||||
|
receivedMessageCache,
|
||||||
|
receivedGroupMessageCache,
|
||||||
|
receivedMessageStatusChangeCache,
|
||||||
|
receivedGroupMessageStatusChangeCache);
|
||||||
if (client.isOnline()) {
|
if (client.isOnline()) {
|
||||||
client.initReceiver(localDB, receivedMessageCache, receivedMessageStatusChangeCache);
|
client.initReceiver(localDB,
|
||||||
|
receivedMessageCache,
|
||||||
|
receivedGroupMessageCache,
|
||||||
|
receivedMessageStatusChangeCache,
|
||||||
|
receivedGroupMessageStatusChangeCache);
|
||||||
loadChatScene();
|
loadChatScene();
|
||||||
}
|
}
|
||||||
} catch (IOException | InterruptedException | TimeoutException e) {
|
} catch (IOException | InterruptedException | TimeoutException e) {
|
||||||
@ -212,6 +231,8 @@ public final class LoginScene {
|
|||||||
|
|
||||||
// Relay unread messages from cache
|
// Relay unread messages from cache
|
||||||
if (receivedMessageCache != null && client.isOnline()) receivedMessageCache.relay();
|
if (receivedMessageCache != null && client.isOnline()) receivedMessageCache.relay();
|
||||||
|
if (receivedGroupMessageCache != null && client.isOnline()) receivedGroupMessageCache.relay();
|
||||||
if (receivedMessageStatusChangeCache != null && client.isOnline()) receivedMessageStatusChangeCache.relay();
|
if (receivedMessageStatusChangeCache != null && client.isOnline()) receivedMessageStatusChangeCache.relay();
|
||||||
|
if (receivedGroupMessageStatusChangeCache != null && client.isOnline()) receivedGroupMessageStatusChangeCache.relay();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,12 +57,14 @@ public class MessageControl extends VBox {
|
|||||||
textLabel.setWrapText(true);
|
textLabel.setWrapText(true);
|
||||||
getChildren().add(textLabel);
|
getChildren().add(textLabel);
|
||||||
// Setting the message status icon and background color
|
// Setting the message status icon and background color
|
||||||
if (message.getRecipientID() != client.getID()) {
|
if (message.getSenderID() == client.getID()) {
|
||||||
final var statusIcon = new ImageView(statusImages.get(message.getStatus()));
|
final var statusIcon = new ImageView(statusImages.get(message.getStatus()));
|
||||||
statusIcon.setPreserveRatio(true);
|
statusIcon.setPreserveRatio(true);
|
||||||
getChildren().add(statusIcon);
|
getChildren().add(statusIcon);
|
||||||
getStyleClass().add("own-message");
|
getStyleClass().add("own-message");
|
||||||
} else getStyleClass().add("received-message");
|
} else {
|
||||||
|
getStyleClass().add("received-message");
|
||||||
|
}
|
||||||
// Adjusting height and weight of the cell to the corresponding ListView
|
// Adjusting height and weight of the cell to the corresponding ListView
|
||||||
paddingProperty().setValue(new Insets(5, 20, 5, 20));
|
paddingProperty().setValue(new Insets(5, 20, 5, 20));
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user