diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/chat/ChatController.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/chat/ChatController.java index 5c8b50a..3a69a45 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/chat/ChatController.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/chat/ChatController.java @@ -3,10 +3,12 @@ package ch.unibas.dmi.dbis.cs108.casono.client.chat; import ch.unibas.dmi.dbis.cs108.casono.client.network.ChatClient; import ch.unibas.dmi.dbis.cs108.casono.client.network.ClientService; import ch.unibas.dmi.dbis.cs108.casono.client.ui.chatui.ChatBoxController; - -import java.util.*; - -import ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing.RequestParameter; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jspecify.annotations.Nullable; @@ -43,9 +45,10 @@ public class ChatController { private static final long REFRESH_TIME = 1000; - private List localUserList; + /** List of all users connected to the server, to safe them locally on the client */ + private final List localUserList; - private Logger logger; + private final Logger logger; /** * Constructor, adds TimerTask to be sent to the server regularly @@ -74,7 +77,7 @@ public class ChatController { } /** - * Method to be activated, if a lobby has be chosen. It will update the UI and add a new + * Method to be activated, if a lobby has been chosen. It will update the UI and add a new * ChatModel to hold the Data for the Lobby Chat * * @param lobbyId @@ -86,7 +89,15 @@ public class ChatController { this.chatBoxController.addChatTab("Lobby", lobbyChatModel); } - /** method to get all messages from the server */ + /** + * Method to process the Messages that got polled by the {@link ChatClient}. + * + *

Case distinction for every message: - Messages identified with ChatType.LOBBY -> check if + * they belong to the users lobby. - Messages identified with ChatType.GLOBAL -> check if the + * target user of that Message is the user, or the message got sent by the user. + * + *

All Messages get added to a particular {@link ChatModel}, if the checks passed. + */ public void receiveMessage() { List newMessages = chatClient.getMessages(); if (!newMessages.isEmpty()) { @@ -112,17 +123,20 @@ public class ChatController { case ChatType.WHISPER: if (msg.target.equals(username) || msg.sender.equals(username)) { ChatKey key; - if(msg.target.equals(username)) { - key = new ChatKey(ChatType.WHISPER, msg.sender); - } else { - key = new ChatKey(ChatType.WHISPER, msg.target); - } - if (chatModelMap.containsKey(key)) { - chatModelMap - .get(key) - .addMessage(msg); + if (msg.target.equals(username)) { + key = new ChatKey(ChatType.WHISPER, msg.sender); } else { - ChatModel chatModel = new ChatModel(ChatType.WHISPER, username, lobbyId, key.targetUser()); + key = new ChatKey(ChatType.WHISPER, msg.target); + } + if (chatModelMap.containsKey(key)) { + chatModelMap.get(key).addMessage(msg); + } else { + ChatModel chatModel = + new ChatModel( + ChatType.WHISPER, + username, + lobbyId, + key.targetUser()); chatBoxController.addWhisperChat(key.targetUser(), chatModel); chatModel.addMessage(msg); } @@ -133,6 +147,14 @@ public class ChatController { } } + /** + * Method to process the usernames of other users connected to the server, after they got polled + * by the {@link ChatClient}. + * + *

Checks for every one of the usernames, if it is already in the list localUserList and if + * not adds them. For every new User added in the List, they get added as an option to start a + * Whisper Chat with. + */ public synchronized void checkWhisperUsers() { List users = chatClient.getUsers(); logger.info(users); diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/ChatClient.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/ChatClient.java index e6fe86e..6a88ed9 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/ChatClient.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/ChatClient.java @@ -2,9 +2,8 @@ package ch.unibas.dmi.dbis.cs108.casono.client.network; import ch.unibas.dmi.dbis.cs108.casono.client.chat.Message; import ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing.RequestParameter; - -import java.util.*; - +import java.util.ArrayList; +import java.util.List; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -71,6 +70,12 @@ public class ChatClient { return messages; } + /** + * Method to poll the usernames of all users currently connected to the server, by sending the + * "LIST_USERS" command. + * + * @return List of all usernames retrieved from the server. + */ public List getUsers() { logger.info("Asking server for list of users"); List users = clientService.processCommand("LIST_USERS"); diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/chatui/ChatBoxController.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/chatui/ChatBoxController.java index 3e5c518..e0a62e6 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/chatui/ChatBoxController.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/chatui/ChatBoxController.java @@ -11,13 +11,14 @@ import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; - import javafx.application.Platform; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.scene.Node; -import javafx.scene.control.*; +import javafx.scene.control.MenuButton; +import javafx.scene.control.MenuItem; +import javafx.scene.control.Tab; +import javafx.scene.control.TabPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.scene.layout.VBox; @@ -77,7 +78,15 @@ public class ChatBoxController { () -> { MenuItem menuItem = new MenuItem(targetUserName); addWhisperChatButton.getItems().add(menuItem); - menuItem.setOnAction(event -> addWhisperChat(targetUserName, new ChatModel(ChatType.WHISPER, username, -1, targetUserName))); + menuItem.setOnAction( + event -> + addWhisperChat( + targetUserName, + new ChatModel( + ChatType.WHISPER, + username, + -1, + targetUserName))); }); } @@ -111,30 +120,36 @@ public class ChatBoxController { public void addChatTab(String title, ChatModel chatModel) { URL resource = getClass().getResource(ressource); FXMLLoader fxmlLoader = new FXMLLoader(resource); - runOnPlatformSynchronized(()-> { - try { - ChatViewController chatViewController = - new ChatViewController(this.chatController, chatModel, this.username); - fxmlLoader.setController(chatViewController); - Node load = fxmlLoader.load(); - VBox.setVgrow(load, Priority.ALWAYS); - VBox vbox = new VBox(); - VBox.setVgrow(vbox, Priority.ALWAYS); - vbox.getChildren().add(load); - chatModel.addListener((msg) -> chatViewController.showMessage(msg)); - Tab newChat = new Tab(title, vbox); - usernameTabMap.put(title, newChat); - this.chatTabPane.getTabs().add(newChat); - this.chatTabPane.getSelectionModel().select(newChat); - return newChat; - } catch (IOException e) { - throw new RuntimeException(e); - } - }); + runOnPlatformSynchronized( + () -> { + try { + ChatViewController chatViewController = + new ChatViewController( + this.chatController, chatModel, this.username); + fxmlLoader.setController(chatViewController); + Node load = fxmlLoader.load(); + VBox.setVgrow(load, Priority.ALWAYS); + VBox vbox = new VBox(); + VBox.setVgrow(vbox, Priority.ALWAYS); + vbox.getChildren().add(load); + chatModel.addListener((msg) -> chatViewController.showMessage(msg)); + Tab newChat = new Tab(title, vbox); + usernameTabMap.put(title, newChat); + this.chatTabPane.getTabs().add(newChat); + this.chatTabPane.getSelectionModel().select(newChat); + return newChat; + } catch (IOException e) { + throw new RuntimeException(e); + } + }); } + /** + * Helper Function to cope with a Threads problem occurring when the main JavaFX Thread performs + * the task to add a new whisper Chat tab, and the task is specified to runLater. + */ public static T runOnPlatformSynchronized(Callable code) { - if(Platform.isFxApplicationThread()) { + if (Platform.isFxApplicationThread()) { try { return code.call(); } catch (Exception e) { @@ -142,13 +157,14 @@ public class ChatBoxController { } } else { CompletableFuture futureLock = new CompletableFuture<>(); - Platform.runLater(() -> { - try { - futureLock.complete(code.call()); - } catch (Exception e) { - futureLock.completeExceptionally(e); - } - }); + Platform.runLater( + () -> { + try { + futureLock.complete(code.call()); + } catch (Exception e) { + futureLock.completeExceptionally(e); + } + }); try { return futureLock.get(); } catch (Throwable e) { @@ -156,5 +172,4 @@ public class ChatBoxController { } } } - } diff --git a/src/test/java/ch/unibas/dmi/dbis/cs108/casono/client/chat/ChatApplication.java b/src/test/java/ch/unibas/dmi/dbis/cs108/casono/client/chat/ChatApplication.java index 722b859..9c81ee7 100644 --- a/src/test/java/ch/unibas/dmi/dbis/cs108/casono/client/chat/ChatApplication.java +++ b/src/test/java/ch/unibas/dmi/dbis/cs108/casono/client/chat/ChatApplication.java @@ -4,12 +4,9 @@ import ch.unibas.dmi.dbis.cs108.casono.client.network.ClientService; import ch.unibas.dmi.dbis.cs108.casono.client.network.CoreClient; import ch.unibas.dmi.dbis.cs108.casono.client.ui.chatui.ChatBoxController; import java.io.IOException; -import java.util.ArrayList; import java.util.List; - import javafx.application.Application; import javafx.fxml.FXMLLoader; -import javafx.scene.Node; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Stage;