diff --git a/Client/src/main/java/dev/wiing/gossip/client/Connection.java b/Client/src/main/java/dev/wiing/gossip/client/Connection.java index 5c5b935..da39185 100644 --- a/Client/src/main/java/dev/wiing/gossip/client/Connection.java +++ b/Client/src/main/java/dev/wiing/gossip/client/Connection.java @@ -84,8 +84,13 @@ public class Connection { } connection.updateConnectionStats(); + + } catch (PacketException e) { + e.printStackTrace(); + } catch (SocketException e) { break; + } catch (IOException e) { e.printStackTrace(); } @@ -212,9 +217,19 @@ public class Connection { } try { - return getPacketManager().readPacket(getSocket().getInputStream()); + Packet packet = getPacketManager().readPacket(getSocket().getInputStream()); + + if (packet.getType() == ErrorPacket.TYPE) { + ErrorPacket errorPacket = (ErrorPacket) packet; + + throw new PacketException(packetManager.getPacketMap().get(errorPacket.getErrorType()), errorPacket.getReason()); + } + + return packet; + } catch (SocketException e) { throw e; + } catch (IOException e) { e.printStackTrace(); return null; @@ -231,6 +246,7 @@ public class Connection { if (packet.getType() != packetType) { queuedPackets.add(packet); + updateConnectionStats(); continue; } diff --git a/Client/src/main/java/dev/wiing/gossip/client/PacketException.java b/Client/src/main/java/dev/wiing/gossip/client/PacketException.java new file mode 100644 index 0000000..fcd8c96 --- /dev/null +++ b/Client/src/main/java/dev/wiing/gossip/client/PacketException.java @@ -0,0 +1,9 @@ +package dev.wiing.gossip.client; + +import dev.wiing.gossip.lib.packets.Packet; + +public class PacketException extends RuntimeException { + public PacketException(Packet packet, String message) { + super("Packet of type=" + packet.getType() + " (" + packet.getClass().getSimpleName() + ") caused an error: " + message); + } +} diff --git a/Client/src/main/java/dev/wiing/gossip/client/controllers/MainChatController.java b/Client/src/main/java/dev/wiing/gossip/client/controllers/MainChatController.java index a926edf..6c1fe81 100644 --- a/Client/src/main/java/dev/wiing/gossip/client/controllers/MainChatController.java +++ b/Client/src/main/java/dev/wiing/gossip/client/controllers/MainChatController.java @@ -13,6 +13,7 @@ import dev.wiing.gossip.lib.packets.MessagePushPacket; import dev.wiing.gossip.lib.packets.TopicJoinPacket; import dev.wiing.gossip.lib.packets.TypingPingPacket; import javafx.application.Platform; +import javafx.collections.ListChangeListener; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.scene.Parent; @@ -109,6 +110,13 @@ public class MainChatController { }); updateTypingMembers(); + + txtCompose.getParagraphs().addListener((ListChangeListener) c -> { + int height = 8 + Math.min(c.getList().size(), 5) * 17; + + txtCompose.setMinHeight(height); + txtCompose.setPrefHeight(height); + }); } private void setTopicName(String name) { @@ -256,7 +264,12 @@ public class MainChatController { } public void updateTypingMembers() { - if (topic.getTypingUsersCount() == 0) { + int typingUsersCount = topic.getTypingUsersCount(); + + if (topic.getTypingUsersReadOnly().stream().anyMatch(user -> user.getUserID() == Connection.getInstance().getSelf().getUserID())) + --typingUsersCount; + + if (typingUsersCount == 0) { lblTyping.setText(""); lblTyping.setMinHeight(0); lblTyping.setMaxHeight(0); @@ -266,7 +279,7 @@ public class MainChatController { lblTyping.setMinHeight(Region.USE_COMPUTED_SIZE); lblTyping.setMaxHeight(Region.USE_COMPUTED_SIZE); - if (topic.getTypingUsersCount() > 3) { + if (typingUsersCount > 3) { lblTyping.setText("Several babblers are writing..."); return; } @@ -274,11 +287,14 @@ public class MainChatController { StringBuilder sb = new StringBuilder(); for (User user : topic.getTypingUsersReadOnly()) { + if (user.getUserID() == Connection.getInstance().getSelf().getUserID()) + continue; + if (!sb.isEmpty()) sb.append(", "); sb.append(user.getUsername()); } - sb.append(topic.getTypingUsersCount() == 1 ? " is" : " are"); + sb.append(typingUsersCount == 1 ? " is" : " are"); sb.append(" writing..."); @@ -289,7 +305,4 @@ public class MainChatController { return Utils.createInstance("views/chat-view.fxml"); } -} - -// TODO: better mutliline input -// 25 + 17 per extra line \ No newline at end of file +} \ No newline at end of file diff --git a/Client/src/main/java/dev/wiing/gossip/client/controllers/MainController.java b/Client/src/main/java/dev/wiing/gossip/client/controllers/MainController.java index 006d043..a1883b2 100644 --- a/Client/src/main/java/dev/wiing/gossip/client/controllers/MainController.java +++ b/Client/src/main/java/dev/wiing/gossip/client/controllers/MainController.java @@ -31,6 +31,7 @@ public class MainController implements Initializable { Topic activeTopic = null; Parent activeTopicElement = null; private final Map> topicMap = new ConcurrentHashMap<>(); + private final Map topicUpdateThreadMap = new ConcurrentHashMap<>(); @FXML private VBox vboxRoot; @@ -121,6 +122,10 @@ public class MainController implements Initializable { for (LongData userID : packet.getUsersJoined()) { User userJoined = Connection.getInstance().getUser(userID.getValue()); topic.addUser(userJoined); + + if (userJoined.getUserID() == Connection.getInstance().getSelf().getUserID()) { + pullTopicMessages(topic); + } } } @@ -139,7 +144,7 @@ public class MainController implements Initializable { User author = Connection.getInstance().getUser(packet.getAuthorID()); - UserMessage userMessage = new UserMessage(packet.getMessageID(), author, topic, packet.getContents()); + UserMessage userMessage = new UserMessage(packet.getMessageID(), author, topic, packet.getContents(), packet.getCreatedTimestamp()); topic.addMessage(userMessage); }); @@ -154,7 +159,7 @@ public class MainController implements Initializable { targetUser = Connection.getInstance().getUser(packet.getUserID()); } - SystemMessage systemMessage = new SystemMessage(packet.getMessageID(), topic, packet.getSystemType(), targetUser, packet.getContent()); + SystemMessage systemMessage = new SystemMessage(packet.getMessageID(), topic, packet.getSystemType(), targetUser, packet.getContent(), packet.getCreatedTimestamp()); topic.addMessage(systemMessage); }); @@ -178,10 +183,12 @@ public class MainController implements Initializable { } private void fetchTopicMessage(Topic topic, long messageID) { + if (topic.hasMessageByID(messageID)) return; + MessageFetchPacket messageFetchPacket = new MessageFetchPacket(); messageFetchPacket.setMessageID(messageID); messageFetchPacket.setTopicID(topic.getId()); - Connection.getInstance().sendPacket(messageFetchPacket); + Connection.getInstance().sendPacketAuthenticated(messageFetchPacket); try { Packet messageFetchResult = Connection.getInstance().nextPacket(MessageDataPacket.TYPE); @@ -195,7 +202,8 @@ public class MainController implements Initializable { messageData.getMessageID(), Connection.getInstance().getUser(messageData.getUserAuthorID()), Connection.getInstance().getTopic(messageData.getTopicID()), - messageData.getUserContents() + messageData.getUserContents(), + messageData.getCreatedTimestamp() ); } else if (messageData.isSystemMessage()) { @@ -204,7 +212,8 @@ public class MainController implements Initializable { Connection.getInstance().getTopic(messageData.getTopicID()), messageData.getSystemType(), Connection.getInstance().getUser(messageData.getSystemUserID()), - messageData.getSystemContents() + messageData.getSystemContents(), + messageData.getCreatedTimestamp() ); } @@ -232,10 +241,6 @@ public class MainController implements Initializable { AnchorPane.setRightAnchor(contentPair.first(), 0.0); contentPair.first().setVisible(false); - MessageListFetchPacket messageListFetchPacket = new MessageListFetchPacket(); - messageListFetchPacket.setTopicID(topic.getId()); - Connection.getInstance().sendPacket(messageListFetchPacket); - Platform.runLater(() -> { paneContent.getChildren().add(contentPair.first()); @@ -246,6 +251,16 @@ public class MainController implements Initializable { setActiveTopic(topic, pair.first()); }); + pullTopicMessages(topic); + } + + private void pullTopicMessages(Topic topic) { + if (!topic.hasUser(Connection.getInstance().getSelf())) return; + + MessageListFetchPacket messageListFetchPacket = new MessageListFetchPacket(); + messageListFetchPacket.setTopicID(topic.getId()); + Connection.getInstance().sendPacketAuthenticated(messageListFetchPacket); + try { Packet messageListFetchResult = Connection.getInstance().nextPacket(MessageListDataPacket.TYPE); if (messageListFetchResult == null) return; @@ -259,6 +274,22 @@ public class MainController implements Initializable { } catch (SocketException e) { throw new RuntimeException(e); } + + long topicID = topic.getId(); + + Thread thread = new Thread(() -> { + while (topicMap.containsKey(topicID)) { + topic.updateAllRelativeTimes(); + + try { + Thread.sleep(30_000); + } catch (InterruptedException ignored) { + } + } + }); + thread.start(); + + topicUpdateThreadMap.put(topicID, thread); } public void setActiveTopic(Topic topic, Parent element) { diff --git a/Client/src/main/java/dev/wiing/gossip/client/controllers/item/MessageItemController.java b/Client/src/main/java/dev/wiing/gossip/client/controllers/item/MessageItemController.java index 1a5a045..7c2e2bf 100644 --- a/Client/src/main/java/dev/wiing/gossip/client/controllers/item/MessageItemController.java +++ b/Client/src/main/java/dev/wiing/gossip/client/controllers/item/MessageItemController.java @@ -6,6 +6,7 @@ import dev.wiing.gossip.client.generic.Pair; import dev.wiing.gossip.client.utils.Utils; import dev.wiing.gossip.lib.models.UserMessage; import dev.wiing.gossip.lib.models.User; +import javafx.application.Platform; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.Parent; @@ -42,6 +43,7 @@ public class MessageItemController implements Initializable { @Override public void initialize(URL location, ResourceBundle resources) { vboxMessages.getChildren().clear(); + lblTimeAgo.setText(""); } public User getAuthor() { @@ -70,6 +72,10 @@ public class MessageItemController implements Initializable { this.lblTag.getStyleClass().add("secondary"); } + if (userMessages.isEmpty()) { + userMessage.attachRelativeTime(data -> Platform.runLater(() -> lblTimeAgo.setText(data))); + } + userMessages.add(userMessage); Label label = new Label(userMessage.getContents()); diff --git a/Client/src/main/java/dev/wiing/gossip/client/controllers/item/SystemMessageItemController.java b/Client/src/main/java/dev/wiing/gossip/client/controllers/item/SystemMessageItemController.java index 9dbedf8..98cf8bd 100644 --- a/Client/src/main/java/dev/wiing/gossip/client/controllers/item/SystemMessageItemController.java +++ b/Client/src/main/java/dev/wiing/gossip/client/controllers/item/SystemMessageItemController.java @@ -3,6 +3,7 @@ package dev.wiing.gossip.client.controllers.item; import dev.wiing.gossip.client.generic.Pair; import dev.wiing.gossip.client.utils.Utils; import dev.wiing.gossip.lib.models.SystemMessage; +import javafx.application.Platform; import javafx.fxml.FXML; import javafx.scene.Node; import javafx.scene.Parent; @@ -32,6 +33,9 @@ public class SystemMessageItemController { public void setSystemMessage(SystemMessage systemMessage) { this.systemMessage = systemMessage; + lblTimeAgo.setText(""); + systemMessage.attachRelativeTime(data -> Platform.runLater(() -> this.lblTimeAgo.setText(data))); + switch (this.systemMessage.getType()) { case SystemMessage.SystemType.USER_JOIN: removeNode(lblPrefix); diff --git a/Client/src/main/resources/dev/wiing/gossip/client/views/chat-view.fxml b/Client/src/main/resources/dev/wiing/gossip/client/views/chat-view.fxml index 89acf10..c05e449 100644 --- a/Client/src/main/resources/dev/wiing/gossip/client/views/chat-view.fxml +++ b/Client/src/main/resources/dev/wiing/gossip/client/views/chat-view.fxml @@ -88,7 +88,7 @@ -