diff --git a/chat/src/main/java/rtgre/chat/ChatController.java b/chat/src/main/java/rtgre/chat/ChatController.java index 5e8e0cc..649be91 100644 --- a/chat/src/main/java/rtgre/chat/ChatController.java +++ b/chat/src/main/java/rtgre/chat/ChatController.java @@ -26,6 +26,7 @@ import net.synedra.validatorfx.Validator; import org.json.JSONObject; import rtgre.chat.graphisme.ContactListViewCell; import rtgre.chat.graphisme.PostListViewCell; +import rtgre.chat.graphisme.RoomListViewCell; import rtgre.chat.net.ChatClient; import rtgre.modeles.*; @@ -68,9 +69,11 @@ public class ChatController implements Initializable { private ContactMap contactMap = new ContactMap(); private ObservableList contactObservableList = FXCollections.observableArrayList(); private ObservableList postsObservableList = FXCollections.observableArrayList(); - Validator validatorLogin = new Validator(); + private Validator validatorLogin = new Validator(); private ChatClient client = null; private PostVector postVector; + private RoomMap roomMap = new RoomMap(); + private ObservableList roomObservableList = FXCollections.observableArrayList(); @Override @@ -103,8 +106,11 @@ public class ChatController implements Initializable { initContactListView(); initPostListView(); + initRoomListView(); contactsListView.getSelectionModel().selectedItemProperty().addListener( (observableValue, previous, selected) -> handleContactSelection((Contact) selected)); + roomsListView.getSelectionModel().selectedItemProperty().addListener( + (observableValue, previous, selected) -> handleRoomSelection((Room) selected)); validatorLogin.createCheck() .dependsOn("login", loginTextField.textProperty()) @@ -113,7 +119,11 @@ public class ChatController implements Initializable { .immediate(); ObservableValue canSendCondition = connectionButton.selectedProperty().not() - .or(contactsListView.getSelectionModel().selectedItemProperty().isNull()); + .or( + roomsListView.getSelectionModel().selectedItemProperty().isNull() + .and(contactsListView.getSelectionModel().selectedItemProperty().isNull()) + ); + sendButton.disableProperty().bind(canSendCondition); messageTextField.disableProperty().bind(canSendCondition); @@ -124,8 +134,22 @@ public class ChatController implements Initializable { /* -------------------------------------- */ } + private void initRoomListView() { + try { + roomsListView.setCellFactory(roomListView -> new RoomListViewCell()); + roomsListView.setItems(roomObservableList); + } catch (Exception e) { + LOGGER.severe(e.getMessage()); + } + } + private void onActionSend(ActionEvent actionEvent) { - String login = getSelectedContactLogin(); + String login = null; + if (!(getSelectedContactLogin() == null)) { + login = getSelectedContactLogin(); + } else if (!(getSelectedRoomName() == null)) { + login = getSelectedRoomName(); + } if (login != null) { Message message = new Message(login, messageTextField.getText()); LOGGER.info("Sending " + message); @@ -173,6 +197,7 @@ public class ChatController implements Initializable { this.contact.setConnected(true); client.sendAuthEvent(contact); + client.sendListRoomEvent(); client.sendEvent(new rtgre.modeles.Event(rtgre.modeles.Event.LIST_CONTACTS, new JSONObject())); initContactListView(); @@ -196,8 +221,10 @@ public class ChatController implements Initializable { private void clearLists() { this.contactMap = new ContactMap(); this.postVector = new PostVector(); + this.roomMap = new RoomMap(); contactObservableList.clear(); postsObservableList.clear(); + roomObservableList.clear(); } private void checkLogin(Check.Context context) { @@ -265,6 +292,19 @@ public class ChatController implements Initializable { return login; } + public String getSelectedRoomName() { + Room room; + String roomName; + try { + room = (Room) roomsListView.getSelectionModel().getSelectedItem(); + roomName = room.getRoomName(); + } catch (Exception e) { + roomName = null; + } + LOGGER.info("Selected room: " + roomName); + return roomName; + } + public Contact getContact() { return contact; } @@ -273,10 +313,33 @@ public class ChatController implements Initializable { return contactMap; } + void handleRoomSelection(Room roomSelected) { + + if (roomSelected != null) { + LOGGER.info("Clic sur " + roomSelected); + } + + if (!contactsListView.getSelectionModel().isEmpty()) { + contactsListView.getSelectionModel().clearSelection(); + } + contact.setCurrentRoom(roomSelected.getRoomName()); + Post postSys = new Post("system", loginTextField.getText(), "Bienvenue dans le salon " + roomSelected); + postsObservableList.clear(); + postsObservableList.add(postSys); + client.sendEvent(new rtgre.modeles.Event("JOIN", new JSONObject().put("room", roomSelected.getRoomName()))); + client.sendListPostEvent(0, roomSelected.toString()); + postListView.refresh(); + } + void handleContactSelection(Contact contactSelected) { if (contactSelected != null) { LOGGER.info("Clic sur " + contactSelected); } + + if (!roomsListView.getSelectionModel().isEmpty()) { + roomsListView.getSelectionModel().clearSelection(); + } + Post postSys = new Post("system", loginTextField.getText(), "Bienvenue dans la discussion avec " + contactSelected.getLogin()); postsObservableList.clear(); postsObservableList.add(postSys); @@ -291,20 +354,41 @@ public class ChatController implements Initializable { handleContEvent(event.getContent()); } else if (event.getType().equals(rtgre.modeles.Event.POST)) { handlePostEvent(event.getContent()); + } else if (event.getType().equals(rtgre.modeles.Event.ROOM)) { + handleRoomEvent(event.getContent()); } else { LOGGER.warning("Unhandled event type: " + event.getType()); this.client.close(); } } - private void handlePostEvent(JSONObject content) { - System.out.println(content.getString("to").equals(((Contact) contactsListView.getSelectionModel().getSelectedItem()).getLogin())); - if (content.getString("to").equals(((Contact) contactsListView.getSelectionModel().getSelectedItem()).getLogin()) || - content.getString("from").equals(loginTextField.getText())) { - postVector.add(Post.fromJson(content)); - postsObservableList.add(Post.fromJson(content)); - postListView.refresh(); + private void handleRoomEvent(JSONObject content) { + LOGGER.info(content.toString()); + Room room = new Room(content.getString("room")); + roomMap.add(room); + roomObservableList.add(room); + roomsListView.refresh(); + } + private void handlePostEvent(JSONObject content) { + if (contactsListView.getSelectionModel().getSelectedItem() != null) { + System.out.println(contactsListView.getSelectionModel().getSelectedItem()); + if (content.getString("to").equals(contact.getLogin()) || + content.getString("from").equals(loginTextField.getText())) { + System.out.println("New message! to:dm"); + postVector.add(Post.fromJson(content)); + postsObservableList.add(Post.fromJson(content)); + postListView.refresh(); + } + } else if (roomsListView.getSelectionModel().getSelectedItem() != null) { + if (content.getString("to").contains("#")) { + if (this.contact.getCurrentRoom().contains(content.getString("to"))) { + System.out.println("New Message! to:room"); + postVector.add(Post.fromJson(content)); + postsObservableList.add(Post.fromJson(content)); + postListView.refresh(); + } + } } } diff --git a/chat/src/main/java/rtgre/chat/graphisme/RoomListViewCell.java b/chat/src/main/java/rtgre/chat/graphisme/RoomListViewCell.java new file mode 100644 index 0000000..1edae79 --- /dev/null +++ b/chat/src/main/java/rtgre/chat/graphisme/RoomListViewCell.java @@ -0,0 +1,87 @@ +package rtgre.chat.graphisme; + +import javafx.embed.swing.SwingFXUtils; +import javafx.geometry.Pos; +import javafx.scene.control.ListCell; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.scene.layout.StackPane; +import javafx.scene.paint.Color; +import javafx.scene.shape.Circle; +import javafx.scene.shape.Rectangle; +import javafx.scene.text.Font; +import javafx.scene.text.Text; +import javafx.scene.text.TextAlignment; +import rtgre.modeles.Contact; +import rtgre.modeles.Room; + +import java.awt.image.BufferedImage; + +import static rtgre.chat.ChatApplication.LOGGER; + +public class RoomListViewCell extends ListCell { + + @Override + protected void updateItem(Room room, boolean empty) { + super.updateItem(room, empty); + if (empty) { + setGraphic(null); + } + else { + // Cas d'un contact + updateRoom(room); + } + } + + + public Color colorFromName(String roomName) { + switch (roomName) { + case "#all": + return Color.CADETBLUE; + case "#juniors": + return Color.FORESTGREEN; + case "#ducks": + return Color.GOLD; + case "#mice": + return Color.LIGHTPINK; + default: + return Color.GRAY; + } + } + + private void updateRoom(Room room) { + LOGGER.finest("Mise à jour de " + room); + + String unreadCountNotif = (room.getUnreadCount() == 0) ? "" : " (%d)".formatted(room.getUnreadCount()); + LOGGER.finest("unread: %s %s".formatted(room.getRoomName(), unreadCountNotif)); + Text roomText = new Text(room.getRoomName() + unreadCountNotif); + roomText.setFont(Font.font(null, 12)); // FontWeight.BOLD, 14)); + roomText.setFill(Color.BLACK); + + + ImageView view = new ImageView(); + Rectangle rectangle = new Rectangle(15, 15, colorFromName(room.getRoomName())); + rectangle.setArcHeight(8.0d); + rectangle.setArcWidth(8.0d); + + StackPane stack = new StackPane(); + stack.getChildren().addAll(rectangle, new Text(room.abbreviation())); + /* + if (contact.getAvatar() != null) { + avatar = SwingFXUtils.toFXImage((BufferedImage) contact.getAvatar(), null); + view = new ImageView(avatar); + }*/ + HBox temp = new HBox(stack); + temp.setAlignment(Pos.CENTER_LEFT); + HBox.setHgrow(roomText, Priority.ALWAYS); + HBox hBox = new HBox(temp, roomText, view); + hBox.setSpacing(5.0); + hBox.setAlignment(Pos.CENTER_LEFT); + + setGraphic(hBox); + } + + +} diff --git a/chat/src/main/java/rtgre/chat/net/ChatClient.java b/chat/src/main/java/rtgre/chat/net/ChatClient.java index aed444e..d094fa0 100644 --- a/chat/src/main/java/rtgre/chat/net/ChatClient.java +++ b/chat/src/main/java/rtgre/chat/net/ChatClient.java @@ -68,6 +68,11 @@ public class ChatClient extends ClientTCP { sendEvent(listPostEvent); } + public void sendListRoomEvent() { + Event listRoomEvent = new Event(Event.LIST_ROOMS, new JSONObject()); + sendEvent(listRoomEvent); + } + public void sendQuitEvent() { Event quitEvent = new Event(Event.QUIT, new JSONObject()); sendEvent(quitEvent); @@ -83,6 +88,7 @@ public class ChatClient extends ClientTCP { LOGGER.info(RED + "Réception: " + message + RST); LOGGER.info(RED + message + RST); if (listener != null) { + System.out.println(message); Platform.runLater(() -> listener.handleEvent(Event.fromJson(message))); } } diff --git a/chat/src/main/java/rtgre/modeles/Contact.java b/chat/src/main/java/rtgre/modeles/Contact.java index bb9ff5a..a0310e9 100644 --- a/chat/src/main/java/rtgre/modeles/Contact.java +++ b/chat/src/main/java/rtgre/modeles/Contact.java @@ -137,4 +137,9 @@ public class Contact { System.out.println("Erreur : " + e.getMessage()); } } + + public void setCurrentRoom(String currentRoom) { + this.currentRoom = currentRoom; + } + } diff --git a/chat/src/main/java/rtgre/modeles/Event.java b/chat/src/main/java/rtgre/modeles/Event.java index 9198f5e..42e646e 100644 --- a/chat/src/main/java/rtgre/modeles/Event.java +++ b/chat/src/main/java/rtgre/modeles/Event.java @@ -13,6 +13,8 @@ public class Event { public static final String LIST_CONTACTS = "LSTC"; public static final String LIST_POSTS = "LSTP"; public static final String SYSTEM = "SYST"; + public static final String LIST_ROOMS = "LSTR"; + public static final String ROOM = "ROOM"; private final String type; private final JSONObject content; diff --git a/chat/src/main/java/rtgre/modeles/Room.java b/chat/src/main/java/rtgre/modeles/Room.java new file mode 100644 index 0000000..433baeb --- /dev/null +++ b/chat/src/main/java/rtgre/modeles/Room.java @@ -0,0 +1,63 @@ +package rtgre.modeles; + +import org.json.JSONObject; + +import java.util.HashSet; +import java.util.Objects; + +public class Room { + protected String roomName; + protected HashSet loginSet; + + + public String getRoomName() { + return roomName; + } + + public HashSet getLoginSet() { + return loginSet; + } + + public Room(String roomName) { + this.roomName = roomName; + this.loginSet = null; + } + + public String abbreviation() { + return this.roomName.split("#")[1].substring(0, 1); + } + + @Override + public String toString() { + return this.roomName; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Room room = (Room) o; + return Objects.equals(roomName, room.roomName); + } + + public void add(String login) { + if (loginSet == null) { + loginSet = new HashSet<>(); + } + loginSet.add(login); + } + + public JSONObject toJsonObject() { + return new JSONObject() + .put("room", this.roomName) + .put("loginSet", this.loginSet); + } + + public String toJson() { + return this.toJsonObject().toString(); + } + + public int getUnreadCount() { + return 0; + } +} diff --git a/chat/src/main/java/rtgre/modeles/RoomMap.java b/chat/src/main/java/rtgre/modeles/RoomMap.java new file mode 100644 index 0000000..eb37b3e --- /dev/null +++ b/chat/src/main/java/rtgre/modeles/RoomMap.java @@ -0,0 +1,16 @@ +package rtgre.modeles; + +import java.util.TreeMap; + +public class RoomMap extends TreeMap { + public void add(Room room) { + this.put(room.getRoomName(), room); + } + + public void loadDefaultRooms() { + this.add(new Room("#all")); + this.add(new Room("#juniors")); + this.add(new Room("#ducks")); + this.add(new Room("#mice")); + } +} diff --git a/chat/src/main/java/rtgre/server/ChatServer.java b/chat/src/main/java/rtgre/server/ChatServer.java index 8a85ebd..0bb40ae 100644 --- a/chat/src/main/java/rtgre/server/ChatServer.java +++ b/chat/src/main/java/rtgre/server/ChatServer.java @@ -24,6 +24,7 @@ public class ChatServer { private Vector clientList; private PostVector postVector; private ContactMap contactMap; + private RoomMap roomMap; static { try { @@ -53,7 +54,9 @@ public class ChatServer { clientList = new Vector<>(); contactMap = new ContactMap(); postVector = new PostVector(); + roomMap = new RoomMap(); contactMap.loadDefaultContacts(); + roomMap.loadDefaultRooms(); } public void close() throws IOException { @@ -254,6 +257,14 @@ public class ChatServer { doListPost(event.getContent()); LOGGER.info("Sending Posts"); return true; + } else if (event.getType().equals(Event.LIST_ROOMS)) { + doListRoom(event.getContent()); + LOGGER.info("Sending Rooms"); + return true; + } else if (event.getType().equals(Event.JOIN)) { + doJoin(event.getContent()); + LOGGER.info("New user joining room!"); + return true; } else if (event.getType().equals(Event.QUIT)) { LOGGER.info("Déconnexion"); return false; @@ -263,15 +274,51 @@ public class ChatServer { } } - private void doListPost(JSONObject content) throws JSONException, IllegalStateException { + private void doJoin(JSONObject content) { + if (content.getString("room").isEmpty()) { + user.setCurrentRoom(null); + } + if (user.getLogin().isEmpty()) { + user.setCurrentRoom(null); + return; + } + if (roomMap.get(content.getString("room")).getLoginSet() == null) { + user.setCurrentRoom(content.getString("room")); + } + else if (roomMap.get(content.getString("room")).getLoginSet().contains(user.getLogin())) { + user.setCurrentRoom(content.getString("room")); + } + } + private void doListRoom(JSONObject content) { if (contactMap.getContact(user.getLogin()).isConnected()) { - if (!contactMap.containsKey(content.getString("select"))) { + for (Room room: roomMap.values()) { + try { + send(new Event("ROOM", room.toJsonObject()).toJson()); + } catch (IOException e) { + throw new IllegalStateException(); + } + } + } + } + + private void doListPost(JSONObject content) throws JSONException, IllegalStateException { + if (contactMap.getContact(user.getLogin()).isConnected()) { + if (!contactMap.containsKey(content.getString("select")) && !roomMap.containsKey(content.getString("select"))) { + System.out.println("!select"); throw new IllegalStateException(); } - for (Post post: postVector.getPostsSince(content.getLong("since"))) { - if (post.getTo().equals(content.getString("select")) || - post.getFrom().equals(content.getString("select"))) { + if (!content.getString("select").contains("#")) { + System.out.println("!#"); + for (Post post : postVector.getPostsSince(content.getLong("since"))) { + if (post.getTo().equals(content.getString("select")) || + post.getFrom().equals(content.getString("select"))) { + sendEventToContact(contactMap.getContact(user.getLogin()), new Event(Event.POST, post.toJsonObject())); + } + } + } else if (user.getCurrentRoom().equals(content.getString("select"))) { + System.out.println("#"); + for (Post post: postVector.getPostsSince(content.getLong("since"))) { sendEventToContact(contactMap.getContact(user.getLogin()), new Event(Event.POST, post.toJsonObject())); } } @@ -280,9 +327,10 @@ public class ChatServer { private void doMessage(JSONObject content) throws JSONException, IllegalStateException { if (contactMap.getContact(user.getLogin()).isConnected()) { - if (content.getString("to").equals(user.getLogin()) || !contactMap.containsKey(content.getString("to"))) { - throw new IllegalStateException(); - } else { + if (content.getString("to").equals(user.getLogin()) || + (!contactMap.containsKey(content.getString("to"))) && !roomMap.containsKey(content.getString("to"))) { + throw new IllegalStateException("IllegalStateException! Cannot Post"); + } if(!content.getString("to").contains("#")) { Post post = new Post( user.getLogin(), Message.fromJson(content) @@ -293,7 +341,24 @@ public class ChatServer { sendEventToContact(contactMap.getContact(post.getTo()), postEvent); postVector.add(post); - LOGGER.info("Fin de doMessage"); + LOGGER.info("Fin de doMessage:dm"); + } else { + Post post = new Post( + user.getLogin(), + Message.fromJson(content) + ); + Event postEvent = new Event("POST", post.toJsonObject()); + + for (ChatClientHandler client: clientList) { + if (client.user.getCurrentRoom() != null) { + if (client.user.getCurrentRoom().equals(content.getString("to"))) { + sendEventToContact(client.user, postEvent); + } + } + } + postVector.add(post); + LOGGER.info("Fin de doMessage:room"); + } } } @@ -322,6 +387,7 @@ public class ChatServer { LOGGER.info("Connexion de " + login); contactMap.getContact(login).setConnected(true); this.user = contactMap.getContact(login); + System.out.println(user.isConnected()); sendAllOtherClients( findClient(contactMap.getContact(login)), new Event("CONT", user.toJsonObject()).toJson() @@ -343,11 +409,11 @@ public class ChatServer { public void sendAllOtherClients(ChatClientHandler fromClient, String message) { for (ChatClientHandler client : clientList) { - if (!client.equals(this)) { + if (!client.equals(fromClient)) { LOGGER.fine(clientList.toString()); LOGGER.fine("Envoi vers [%s] : %s".formatted(client.getIpPort(), message)); try { - client.send("[%s] %s".formatted(fromClient.getIpPort(), message)); + client.send(message); } catch (Exception e) { LOGGER.severe("[%s] %s".formatted(client.getIpPort(), e)); client.close(); @@ -371,6 +437,7 @@ public class ChatServer { sock.close(); removeClient(this); user.setConnected(false); + contactMap.get(user.getLogin()).setConnected(false); sendEventToAllContacts(new Event(Event.CONT, user.toJsonObject())); } catch (IOException e) { throw new RuntimeException(e);