From 3e352ef8015483eefc7653c00eabe5cbfb517e16 Mon Sep 17 00:00:00 2001 From: bouclyma Date: Wed, 11 Dec 2024 12:42:16 +0100 Subject: [PATCH] feat(post): refresh de la liste des messages on-cick, ajout classe PostVector --- .../main/java/rtgre/chat/ChatController.java | 12 +- .../chat/graphisme/PostListViewCell.java | 88 ++++++++++++++ .../main/java/rtgre/modeles/PostVector.java | 27 +++++ .../java/rtgre/modeles/PostVectorTest.java | 112 ++++++++++++++++++ 4 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 chat/src/main/java/rtgre/chat/graphisme/PostListViewCell.java create mode 100644 chat/src/main/java/rtgre/modeles/PostVector.java create mode 100644 chat/src/test/java/rtgre/modeles/PostVectorTest.java diff --git a/chat/src/main/java/rtgre/chat/ChatController.java b/chat/src/main/java/rtgre/chat/ChatController.java index df69ee5..aac96e3 100644 --- a/chat/src/main/java/rtgre/chat/ChatController.java +++ b/chat/src/main/java/rtgre/chat/ChatController.java @@ -23,6 +23,7 @@ import net.synedra.validatorfx.Check; import net.synedra.validatorfx.TooltipWrapper; import net.synedra.validatorfx.Validator; import rtgre.chat.graphisme.ContactListViewCell; +import rtgre.chat.graphisme.PostListViewCell; import rtgre.modeles.Contact; import rtgre.modeles.ContactMap; import rtgre.modeles.Message; @@ -196,6 +197,7 @@ public class ChatController implements Initializable { } private void initPostListView() { try { + postListView.setCellFactory(postListView -> new PostListViewCell(this)); postListView.setItems(postsObservableList); } catch (Exception e) { LOGGER.severe(e.getMessage()); @@ -216,12 +218,20 @@ public class ChatController implements Initializable { return login; } + public Contact getContact() { + return contact; + } + + public ContactMap getContactsMap() { + return contactMap; + } + void handleContactSelection(Contact contactSelected) { if (contactSelected != null) { LOGGER.info("Clic sur " + contactSelected); } Post postSys = new Post("system", contactSelected.getLogin(), "Bienvenue dans la discussion avec " + contactSelected.getLogin()); postsObservableList.add(postSys); - + postListView.refresh(); } } \ No newline at end of file diff --git a/chat/src/main/java/rtgre/chat/graphisme/PostListViewCell.java b/chat/src/main/java/rtgre/chat/graphisme/PostListViewCell.java new file mode 100644 index 0000000..164c21b --- /dev/null +++ b/chat/src/main/java/rtgre/chat/graphisme/PostListViewCell.java @@ -0,0 +1,88 @@ +package rtgre.chat.graphisme; + +import rtgre.chat.ChatController; +import javafx.embed.swing.SwingFXUtils; +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.control.ListCell; +import javafx.scene.image.ImageView; +import javafx.scene.layout.*; +import javafx.scene.paint.Color; +import javafx.scene.text.*; +import rtgre.modeles.Contact; +import rtgre.modeles.Post; + +import java.awt.image.BufferedImage; +import java.util.Date; + +public class PostListViewCell extends ListCell { + + /** Controller de l'application */ + ChatController controller; + + public PostListViewCell(ChatController controller) { + this.controller = controller; + } + + /** + * Callback déclenchée à chaque modification d'un objet d'une liste d'observable. + * + * @param post Le post + * @param empty La liste de cellule doit-elle être complètement remise à zéro ? + */ + @Override + protected void updateItem(Post post, boolean empty) { + super.updateItem(post, empty); + if (empty) { + setGraphic(null); + } + else { + updatePost(post); + } + } + + void updatePost(Post post) { + + Text datetimeText = new Text("\n%1$td/%1$tm/%1$tY %1$tH:%1$tM:%1$tS\n".formatted(new Date(post.getTimestamp()))); + datetimeText.setFont(Font.font(null, FontPosture.ITALIC, 8)); + + Text nicknameText = new Text(post.getFrom() + ": "); + nicknameText.setFill(Color.DARKBLUE); + nicknameText.setFont(Font.font(null, FontWeight.BOLD, 14)); + nicknameText.setFill(Color.BLUEVIOLET); + + Text msgText = new Text(post.getBody()); + + // L'émetteur du message + Contact c = this.controller.getContactsMap().get(post.getFrom()); + ImageView avatar = new ImageView(); + if (c != null) { + avatar = new ImageView(SwingFXUtils.toFXImage((BufferedImage) c.getAvatar(), null)); + avatar.setFitWidth(20); + avatar.setFitHeight(20); + } + + TextFlow tf = new TextFlow((Node) datetimeText, avatar, nicknameText, msgText); + tf.maxWidthProperty().bind(getListView().widthProperty().multiply(0.8)); + HBox hBox = new HBox(tf); + hBox.maxWidthProperty().bind(getListView().widthProperty()); + String login; + + try { + login = this.controller.getContact().getLogin(); + } + catch (Exception e) { + login = "???"; + } + + if (post.getFrom().equals(login)) { + tf.setBackground(Background.fill(Color.web("#EEF"))); + hBox.setAlignment(Pos.CENTER_RIGHT); + } else { + tf.setBackground(Background.fill(Color.web("#FEE"))); + hBox.setAlignment(Pos.CENTER_LEFT); + } + setGraphic(hBox); + getListView().scrollTo(getListView().getItems().size() - 1); + } +} \ No newline at end of file diff --git a/chat/src/main/java/rtgre/modeles/PostVector.java b/chat/src/main/java/rtgre/modeles/PostVector.java new file mode 100644 index 0000000..1db1105 --- /dev/null +++ b/chat/src/main/java/rtgre/modeles/PostVector.java @@ -0,0 +1,27 @@ +package rtgre.modeles; + +import java.util.UUID; +import java.util.Vector; + +public class PostVector extends Vector { + + public Post getPostById(UUID uuid) { + for (Post post : this) { + if (post.id == uuid) { + return post; + } + } + return null; + } + + public Vector getPostsSince(long timestamp) { + Vector posts = new Vector<>(); + for (Post post : this) { + if (post.timestamp > timestamp) { + posts.add(post); + } + } + return posts; + } + +} diff --git a/chat/src/test/java/rtgre/modeles/PostVectorTest.java b/chat/src/test/java/rtgre/modeles/PostVectorTest.java new file mode 100644 index 0000000..81affa9 --- /dev/null +++ b/chat/src/test/java/rtgre/modeles/PostVectorTest.java @@ -0,0 +1,112 @@ +package rtgre.modeles; + +import javafx.geometry.Pos; +import org.junit.jupiter.api.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; +import java.util.Vector; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +class PostVectorTest { + + static Class classe = PostVector.class; + static String module = "rtgre.modeles"; + + @DisplayName("01-Structure") + @Nested + class Structure { + + static List constructeursSignatures; + static List methodesSignatures; + + @BeforeAll + static void init() { + Constructor[] constructeurs = classe.getConstructors(); + constructeursSignatures = Arrays.stream(constructeurs).map(Constructor::toString).collect(Collectors.toList()); + Method[] methodes = classe.getDeclaredMethods(); + methodesSignatures = Arrays.stream(methodes).map(Method::toString).collect(Collectors.toList()); + } + + @Test + @DisplayName("Heritage") + void testHeritage() { + PostVector pv = new PostVector(); + assertInstanceOf(Vector.class, pv, "Doit hériter de Vector"); + } + + @Test + @DisplayName("Nbre attributs") + void testDeclarationNbreAttributs() { + Field[] fields = classe.getDeclaredFields(); + assertEquals(0, fields.length, "Ne doit pas posséder d'attributs"); + } + + /** Méthodes */ + static Stream methodesProvider1() { + return Stream.of( + arguments("getPostById", "public %s.Post rtgre.modeles.PostVector.getPostById(java.util.UUID)"), + arguments("getPostsSince", "public java.util.Vector %s.PostVector.getPostsSince(long)") + ); + } + @DisplayName("Déclaration des méthodes (base)") + @ParameterizedTest + @MethodSource("methodesProvider1") + void testDeclarationMethodes1(String nom, String signature) { + Assertions.assertTrue(methodesSignatures.contains(String.format(signature, module, module)), + String.format("Méthode non déclarée : doit être %s\nalors que sont déclarés %s", + signature, methodesSignatures)); + } + + + } + + @DisplayName("02-Méthodes") + @Nested + class MethodesPostVectorTest { + @Test + void getPostById() { + PostVector pv = new PostVector(); + UUID uuid1 = UUID.fromString("a821f534-b63a-4006-bbe1-eab7c4ff834e"); + UUID uuid2 = UUID.fromString("b932f534-b63a-4006-bbe1-eab7c4ff834e"); + Post p1 = new Post(uuid1, 70000, "fifi", "riri", "message"); + Post p2 = new Post(uuid2, 70000, "donald", "mickey", "message"); + + pv.add(new Post("fifi", "riri", "message")); + pv.add(new Post("donald", "mickey", "message")); + pv.add(p1); + pv.add(new Post("fifi", "riri", "message")); + pv.add(p2); + + Assertions.assertEquals(p1, pv.getPostById(uuid1), "Récupération post erronée"); + Assertions.assertEquals(p2, pv.getPostById(uuid2), "Récupération post erronée"); + } + + @Test + void getPostsSince() { + PostVector pv = new PostVector(); + pv.add(new Post(UUID.randomUUID(), 70000, "fifi", "riri", "message")); + pv.add(new Post(UUID.randomUUID(), 70010, "donald", "mickey", "message")); + pv.add(new Post(UUID.randomUUID(), 70020, "fifi", "riri", "message")); + pv.add(new Post(UUID.randomUUID(), 80000, "fifi", "riri", "message")); + pv.add(new Post(UUID.randomUUID(), 80020, "fifi", "riri", "message")); + + Vector expected = new Vector<>(); + expected.add(pv.get(2)); expected.add(pv.get(3)); expected.add(pv.get(4)); + + Vector res = pv.getPostsSince(70015); + assertEquals(expected, res, "Extraction erronée"); + } + } +} \ No newline at end of file