From d9b1ca10bc112d241facaf179b1e1145141d6b1f Mon Sep 17 00:00:00 2001 From: Eric Thilen Date: Tue, 4 Nov 2025 14:27:41 +0100 Subject: [PATCH 01/13] commit --- .../java/com/example/HelloController.java | 12 +++- src/main/java/com/example/HelloModel.java | 43 +++++++++++++ src/main/java/com/example/ManyParameters.java | 19 ++++++ .../com/example/ManyParametersBuilder.java | 38 ++++++++++++ src/main/java/com/example/NtfyConnection.java | 11 ++++ .../java/com/example/NtfyConnectionImpl.java | 61 +++++++++++++++++++ src/main/java/com/example/NtfyMessageDto.java | 7 +++ src/main/java/com/example/Singelton.java | 14 +++++ src/main/java/module-info.java | 4 ++ src/test/java/com/example/HelloModelTest.java | 28 +++++++++ .../java/com/example/NtfyConnectionSpy.java | 19 ++++++ 11 files changed, 254 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/example/ManyParameters.java create mode 100644 src/main/java/com/example/ManyParametersBuilder.java create mode 100644 src/main/java/com/example/NtfyConnection.java create mode 100644 src/main/java/com/example/NtfyConnectionImpl.java create mode 100644 src/main/java/com/example/NtfyMessageDto.java create mode 100644 src/main/java/com/example/Singelton.java create mode 100644 src/test/java/com/example/HelloModelTest.java create mode 100644 src/test/java/com/example/NtfyConnectionSpy.java diff --git a/src/main/java/com/example/HelloController.java b/src/main/java/com/example/HelloController.java index fdd160a0..f83a10bf 100644 --- a/src/main/java/com/example/HelloController.java +++ b/src/main/java/com/example/HelloController.java @@ -1,14 +1,17 @@ package com.example; +import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.scene.control.Label; +import javafx.scene.control.ListView; /** * Controller layer: mediates between the view (FXML) and the model. */ public class HelloController { - private final HelloModel model = new HelloModel(); + private final HelloModel model = new HelloModel(null); + public ListView messageView; @FXML private Label messageLabel; @@ -18,5 +21,10 @@ private void initialize() { if (messageLabel != null) { messageLabel.setText(model.getGreeting()); } + messageView.setItems(model.getMessages()); } -} + + public void sendMessage(ActionEvent actionEvent) { + model.sendMessage(); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/HelloModel.java b/src/main/java/com/example/HelloModel.java index 385cfd10..43273c2d 100644 --- a/src/main/java/com/example/HelloModel.java +++ b/src/main/java/com/example/HelloModel.java @@ -1,9 +1,43 @@ + package com.example; +import javafx.application.Platform; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; + /** * Model layer: encapsulates application data and business logic. */ public class HelloModel { + + private final NtfyConnection connection; + + private final ObservableList messages = FXCollections.observableArrayList(); + private final StringProperty messageToSend = new SimpleStringProperty(); + + public HelloModel(NtfyConnection connection) { + receiveMessage(); + this.connection = connection; + } + + public ObservableList getMessages() { + return messages; + } + + public String getMessageToSend() { + return messageToSend.get(); + } + + public StringProperty messageToSendProperty() { + return messageToSend; + } + + public void setMessageToSend(String message) { + messageToSend.set(message); + } + /** * Returns a greeting based on the current Java and JavaFX versions. */ @@ -12,4 +46,13 @@ public String getGreeting() { String javafxVersion = System.getProperty("javafx.version"); return "Hello, JavaFX " + javafxVersion + ", running on Java " + javaVersion + "."; } + + public void sendMessage() { + connection.send(messageToSend.get()); + + } + + public void receiveMessage() { + connection.receive(m -> Platform.runLater(() -> messages.add(m))); + } } diff --git a/src/main/java/com/example/ManyParameters.java b/src/main/java/com/example/ManyParameters.java new file mode 100644 index 00000000..9efe0e71 --- /dev/null +++ b/src/main/java/com/example/ManyParameters.java @@ -0,0 +1,19 @@ +package com.example; + +public class ManyParameters { + + public ManyParameters(String computerName, int timeout, + String method, int size, byte[] data) { + + } + + + static void main() { + ManyParametersBuilder builder = new ManyParametersBuilder(); + builder + .setComputerName("localhost") //Fluent API + .setTimeout(10) + .setSize(0) + .createManyParameters(); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/ManyParametersBuilder.java b/src/main/java/com/example/ManyParametersBuilder.java new file mode 100644 index 00000000..fd49920d --- /dev/null +++ b/src/main/java/com/example/ManyParametersBuilder.java @@ -0,0 +1,38 @@ +package com.example; + +public class ManyParametersBuilder { + private String computerName; + private int timeout = 0; + private String method; + private int size = 0; + private byte[] data = null; + + public ManyParametersBuilder setComputerName(String computerName) { + this.computerName = computerName; + return this; + } + + public ManyParametersBuilder setTimeout(int timeout) { + this.timeout = timeout; + return this; + } + + public ManyParametersBuilder setMethod(String method) { + this.method = method; + return this; + } + + public ManyParametersBuilder setSize(int size) { + this.size = size; + return this; + } + + public ManyParametersBuilder setData(byte[] data) { + this.data = data; + return this; + } + + public ManyParameters createManyParameters() { + return new ManyParameters(computerName, timeout, method, size, data); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/NtfyConnection.java b/src/main/java/com/example/NtfyConnection.java new file mode 100644 index 00000000..3b2f0967 --- /dev/null +++ b/src/main/java/com/example/NtfyConnection.java @@ -0,0 +1,11 @@ +package com.example; + +import java.util.function.Consumer; + +public interface NtfyConnection { + + public boolean send(String message); + + public void receive(Consumer messageHandler); + +} diff --git a/src/main/java/com/example/NtfyConnectionImpl.java b/src/main/java/com/example/NtfyConnectionImpl.java new file mode 100644 index 00000000..0db420b8 --- /dev/null +++ b/src/main/java/com/example/NtfyConnectionImpl.java @@ -0,0 +1,61 @@ +package com.example; + +import io.github.cdimascio.dotenv.Dotenv; +import tools.jackson.databind.ObjectMapper; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.Objects; +import java.util.function.Consumer; + +public class NtfyConnectionImpl implements NtfyConnection { + + private final HttpClient http = HttpClient.newHttpClient(); + private final String hostName; + private final ObjectMapper mapper = new ObjectMapper(); + + public NtfyConnectionImpl() { + Dotenv dotenv = Dotenv.load(); + hostName = Objects.requireNonNull(dotenv.get("HOST_NAME")); + } + + @Override + public boolean send(String message) { + HttpRequest httpRequest = HttpRequest.newBuilder() + .POST(HttpRequest.BodyPublishers.ofString("Hello World đŸŒ")) + .header("Cache", "no") + .uri(URI.create(hostName + "/mytopic")) + .build(); + try { + //Todo: handle long blocking send requests to not freeze the JavaFX thread + //1. Use thread send message? + //2. Use async? + var reponse = http.send(httpRequest, HttpResponse.BodyHandlers.discarding()); + return true; + } catch (IOException e) { + System.out.println("Error sending message"); + } catch (InterruptedException e) { + System.out.println("Interruped sending message"); + } + return false; + } + + @Override + public void receive(Consumer messageHandler) { + HttpRequest httpRequest = HttpRequest.newBuilder() + .GET() + .uri(URI.create(hostName + "/mytopic/json")) + .build(); + + http.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofLines()) + .thenAccept(response -> response.body() + .map(s -> + mapper.readValue(s, NtfyMessageDto.class)) + .filter(message -> message.event().equals("message")) + .peek(System.out::println) + .forEach(messageHandler)); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/NtfyMessageDto.java b/src/main/java/com/example/NtfyMessageDto.java new file mode 100644 index 00000000..697cccac --- /dev/null +++ b/src/main/java/com/example/NtfyMessageDto.java @@ -0,0 +1,7 @@ +package com.example; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@JsonIgnoreProperties(ignoreUnknown = true) +public record NtfyMessageDto(String id, long time, String event, String topic, String message) { +} \ No newline at end of file diff --git a/src/main/java/com/example/Singelton.java b/src/main/java/com/example/Singelton.java new file mode 100644 index 00000000..b3685a01 --- /dev/null +++ b/src/main/java/com/example/Singelton.java @@ -0,0 +1,14 @@ +package com.example; + +public class Singelton { + + private final static Singelton instance = new Singelton(); + + private Singelton(){ + + } + + public static Singelton getInstance(){ + return instance; + } +} \ No newline at end of file diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 71574a27..89e041cb 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,6 +1,10 @@ module hellofx { requires javafx.controls; requires javafx.fxml; + requires io.github.cdimascio.dotenv.java; + requires java.net.http; + requires tools.jackson.databind; + requires javafx.graphics; opens com.example to javafx.fxml; exports com.example; diff --git a/src/test/java/com/example/HelloModelTest.java b/src/test/java/com/example/HelloModelTest.java new file mode 100644 index 00000000..fb38ca0d --- /dev/null +++ b/src/test/java/com/example/HelloModelTest.java @@ -0,0 +1,28 @@ +package com.example; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class HelloModelTest { + + @Test + @DisplayName("Given a model with messageToSend when calling sendMessage then send method on connection should be called") + void sendMessageCallsConnectionWithMessageToSend() { + //Arrange Given + var spy = new NtfyConnectionSpy(); + var model = new HelloModel(spy); + model.setMessageToSend("Hello World"); + //Act When + model.sendMessage(); + //Assert Then + assertThat(spy.message).isEqualTo("Hello World"); + } + + @Test + void sendMessageToFakeServer() { + var con = new NtfyConnectionImpl(); + + } +} \ No newline at end of file diff --git a/src/test/java/com/example/NtfyConnectionSpy.java b/src/test/java/com/example/NtfyConnectionSpy.java new file mode 100644 index 00000000..3f86453f --- /dev/null +++ b/src/test/java/com/example/NtfyConnectionSpy.java @@ -0,0 +1,19 @@ +package com.example; + +import java.util.function.Consumer; + +public class NtfyConnectionSpy implements NtfyConnection{ + + String message; + + @Override + public boolean send(String message) { + this.message = message; + return true; + } + + @Override + public void receive(Consumer messageHandler) { + + } +} \ No newline at end of file From 6dab50c72aab9b411050b7d6e94b33277746ad91 Mon Sep 17 00:00:00 2001 From: Eric Thilen Date: Tue, 11 Nov 2025 10:45:15 +0100 Subject: [PATCH 02/13] commit --- pom.xml | 20 +++++++++++++++++-- .../java/com/example/NtfyConnectionImpl.java | 4 ++++ src/test/java/com/example/HelloModelTest.java | 13 ++++++++++-- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index c40f667e..f410eea0 100644 --- a/pom.xml +++ b/pom.xml @@ -45,6 +45,22 @@ javafx-fxml ${javafx.version} + + io.github.cdimascio + dotenv-java + 3.2.0 + + + tools.jackson.core + jackson-databind + 3.0.1 + + + org.wiremock + wiremock + 4.0.0-beta.15 + test + @@ -55,7 +71,7 @@ com.example.HelloFX - + javafx true @@ -65,4 +81,4 @@ - + \ No newline at end of file diff --git a/src/main/java/com/example/NtfyConnectionImpl.java b/src/main/java/com/example/NtfyConnectionImpl.java index 0db420b8..f2a59a7c 100644 --- a/src/main/java/com/example/NtfyConnectionImpl.java +++ b/src/main/java/com/example/NtfyConnectionImpl.java @@ -22,6 +22,10 @@ public NtfyConnectionImpl() { hostName = Objects.requireNonNull(dotenv.get("HOST_NAME")); } + public NtfyConnectionImpl(String hostName) { + this.hostName = hostName; + } + @Override public boolean send(String message) { HttpRequest httpRequest = HttpRequest.newBuilder() diff --git a/src/test/java/com/example/HelloModelTest.java b/src/test/java/com/example/HelloModelTest.java index fb38ca0d..a366e99d 100644 --- a/src/test/java/com/example/HelloModelTest.java +++ b/src/test/java/com/example/HelloModelTest.java @@ -1,8 +1,10 @@ package com.example; +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import static com.github.tomakehurst.wiremock.client.WireMock.*; import static org.assertj.core.api.Assertions.assertThat; class HelloModelTest { @@ -21,8 +23,15 @@ void sendMessageCallsConnectionWithMessageToSend() { } @Test - void sendMessageToFakeServer() { - var con = new NtfyConnectionImpl(); + void sendMessageToFakeServer(WireMockRuntimeInfo wireMockRuntimeInfo) { + var con = new NtfyConnectionImpl("https://ntfy.fungover.org" + wmRuntimeInfo.getHttpPort()); + var model = new HelloModel(con); + model.setMessageToSend("Hello World"); + stubFor(post("/mytopic").willReturn(ok())) + + model.sendMessage(); + verify(postRequestedFor(urlEqualTo("/mytopic")) + .withRequestBody(matching("Hello World"))); } } \ No newline at end of file From abcb23b1372204abb8d83cc73e024ba332944e5f Mon Sep 17 00:00:00 2001 From: Eric Thilen Date: Fri, 14 Nov 2025 21:37:33 +0100 Subject: [PATCH 03/13] Fixed hellocontroller --- .../java/com/example/HelloController.java | 60 ++++++++++++++----- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/example/HelloController.java b/src/main/java/com/example/HelloController.java index f83a10bf..8d995951 100644 --- a/src/main/java/com/example/HelloController.java +++ b/src/main/java/com/example/HelloController.java @@ -1,30 +1,58 @@ package com.example; -import javafx.event.ActionEvent; +import javafx.application.Platform; import javafx.fxml.FXML; -import javafx.scene.control.Label; -import javafx.scene.control.ListView; +import javafx.scene.control.TextArea; +import javafx.scene.control.TextField; +import javafx.stage.FileChooser; +import javafx.scene.control.Button; + +import java.io.File; -/** - * Controller layer: mediates between the view (FXML) and the model. - */ public class HelloController { - private final HelloModel model = new HelloModel(null); - public ListView messageView; + @FXML + private TextArea chatArea; + + @FXML + private TextField inputField; @FXML - private Label messageLabel; + private Button sendButton; + + @FXML + private Button attachButton; + + private HelloModel model; @FXML - private void initialize() { - if (messageLabel != null) { - messageLabel.setText(model.getGreeting()); + public void initialize() { + // LĂ€ser BACKEND_URL och TOPIC frĂ„n .env via HelloModel + model = new HelloModel(); + + // Lyssna pĂ„ inkommande meddelanden + model.listen(msg -> { + Platform.runLater(() -> chatArea.appendText(msg + "\n")); + System.out.println("đŸ“© Mottaget: " + msg); + }); + } + + @FXML + protected void onSendButtonClick() { + String message = inputField.getText().trim(); + if (!message.isEmpty()) { + model.sendMessage(message); + inputField.clear(); } - messageView.setItems(model.getMessages()); } - public void sendMessage(ActionEvent actionEvent) { - model.sendMessage(); + @FXML + protected void onAttachFileClick() { + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("VĂ€lj en fil att skicka"); + File file = fileChooser.showOpenDialog(chatArea.getScene().getWindow()); + if (file != null) { + model.sendFile(file); + } } -} \ No newline at end of file +} From 0ef4d6e94ed922e23e7d6f5c098e0ffbfafd42a8 Mon Sep 17 00:00:00 2001 From: Eric Thilen Date: Fri, 14 Nov 2025 21:37:43 +0100 Subject: [PATCH 04/13] Fixed Hello Model --- src/main/java/com/example/HelloModel.java | 152 +++++++++++++++++----- 1 file changed, 117 insertions(+), 35 deletions(-) diff --git a/src/main/java/com/example/HelloModel.java b/src/main/java/com/example/HelloModel.java index 43273c2d..240f413d 100644 --- a/src/main/java/com/example/HelloModel.java +++ b/src/main/java/com/example/HelloModel.java @@ -1,58 +1,140 @@ - package com.example; -import javafx.application.Platform; -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.property.StringProperty; -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; +import io.github.cdimascio.dotenv.Dotenv; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.JsonNode; + +import java.io.File; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.file.Files; +import java.util.concurrent.CompletableFuture; -/** - * Model layer: encapsulates application data and business logic. - */ public class HelloModel { - private final NtfyConnection connection; + private static final ObjectMapper mapper = new ObjectMapper(); - private final ObservableList messages = FXCollections.observableArrayList(); - private final StringProperty messageToSend = new SimpleStringProperty(); + private final HttpClient client = HttpClient.newHttpClient(); + private final String topic; + private final String backendUrl; - public HelloModel(NtfyConnection connection) { - receiveMessage(); - this.connection = connection; + /** Standardkonstruktor som lĂ€ser frĂ„n .env */ + public HelloModel() { + Dotenv dotenv = Dotenv.configure().ignoreIfMissing().load(); + this.backendUrl = dotenv.get("BACKEND_URL", System.getenv("BACKEND_URL")); + this.topic = dotenv.get("TOPIC", System.getenv("TOPIC")); + if (backendUrl == null || topic == null) { + throw new IllegalStateException("BACKEND_URL eller TOPIC saknas i .env"); + } } - public ObservableList getMessages() { - return messages; + /** Alternativ konstruktor för tester */ + HelloModel(String topic, String backendUrl) { + if (backendUrl == null || backendUrl.isBlank()) { + throw new IllegalArgumentException("backendUrl must not be null/blank"); + } + this.backendUrl = backendUrl; + this.topic = topic; } - public String getMessageToSend() { - return messageToSend.get(); - } + public void sendMessage(String message) { + String sender = "[Eric Chat App]"; + String fullMessage = sender + " " + message; + + String json = "{\"message\": \"" + fullMessage.replace("\"", "\\\"") + "\"}"; + String url = backendUrl + "/" + topic; + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(url)) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofString(json)) + .build(); - public StringProperty messageToSendProperty() { - return messageToSend; + client.sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenAccept(response -> { + if (response.statusCode() >= 300) { + System.err.println("⚠ Misslyckades att skicka: " + response.statusCode() + " - " + response.body()); + } + }) + .exceptionally(ex -> { + System.err.println("⚠ NĂ€tverksfel vid sendMessage: " + ex.getMessage()); + return null; + }); } - public void setMessageToSend(String message) { - messageToSend.set(message); + public void sendFile(File file) { + try { + String url = backendUrl + "/" + topic; + String contentType = Files.probeContentType(file.toPath()); + if (contentType == null) contentType = "application/octet-stream"; + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(url)) + .header("Content-Type", contentType) + .header("X-Filename", file.getName()) + .header("Title", "File: " + file.getName()) + .POST(HttpRequest.BodyPublishers.ofFile(file.toPath())) + .build(); + + client.sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenAccept(response -> { + if (response.statusCode() >= 300) { + System.err.println("⚠ Filupload misslyckades: " + response.statusCode() + " - " + response.body()); + } else { + System.out.println("✅ Fil skickad: " + file.getName()); + } + }) + .exceptionally(ex -> { + System.err.println("⚠ NĂ€tverksfel vid sendFile: " + ex.getMessage()); + return null; + }); + } catch (Exception e) { + System.err.println("⚠ Kunde inte lĂ€sa/skicka fil: " + e.getMessage()); + } } - /** - * Returns a greeting based on the current Java and JavaFX versions. - */ - public String getGreeting() { - String javaVersion = System.getProperty("java.version"); - String javafxVersion = System.getProperty("javafx.version"); - return "Hello, JavaFX " + javafxVersion + ", running on Java " + javaVersion + "."; + public CompletableFuture listen(MessageHandler handler) { + String url = backendUrl + "/" + topic + "/json"; + HttpRequest request = HttpRequest.newBuilder().uri(URI.create(url)).build(); + + return client.sendAsync(request, HttpResponse.BodyHandlers.ofLines()) + .thenAccept(response -> response.body().forEach(line -> { + String parsed = parseIncomingLine(line); + if (!parsed.isEmpty()) { + handler.onMessage(parsed); + System.out.println("đŸ“© Meddelande: " + parsed); + } + })) + .exceptionally(ex -> { + System.err.println("⚠ NĂ€tverksfel vid listen: " + ex.getMessage()); + return null; + }); } - public void sendMessage() { - connection.send(messageToSend.get()); + String parseIncomingLine(String line) { + try { + JsonNode outer = mapper.readTree(line); + String raw = outer.path("message").asText(""); + if (raw.isEmpty()) return ""; + + String clean = raw.startsWith("{") + ? mapper.readTree(raw).path("message").asText(raw) + : raw; + + if (!clean.contains("[Eric Chat App]") && !clean.contains("[Javafx-chat]")) { + clean = "[Javafx-chat] " + clean; + } + return "💬 " + clean; + } catch (Exception e) { + System.err.println("⚠ Kunde inte tolka rad: " + line + " | " + e.getMessage()); + return ""; + } } - public void receiveMessage() { - connection.receive(m -> Platform.runLater(() -> messages.add(m))); + public interface MessageHandler { + void onMessage(String message); } } From baa5a45c57e800050ffdd2d0291106ee0904b858 Mon Sep 17 00:00:00 2001 From: Eric Thilen Date: Fri, 14 Nov 2025 21:38:00 +0100 Subject: [PATCH 05/13] Finished with fx --- src/main/java/com/example/HelloFX.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/example/HelloFX.java b/src/main/java/com/example/HelloFX.java index 96bdc5ca..10edc2cb 100644 --- a/src/main/java/com/example/HelloFX.java +++ b/src/main/java/com/example/HelloFX.java @@ -10,10 +10,18 @@ public class HelloFX extends Application { @Override public void start(Stage stage) throws Exception { + // Ladda FXML FXMLLoader fxmlLoader = new FXMLLoader(HelloFX.class.getResource("hello-view.fxml")); Parent root = fxmlLoader.load(); + + // Skapa scenen Scene scene = new Scene(root, 640, 480); - stage.setTitle("Hello MVC"); + + // Koppla in CSS-styling + scene.getStylesheets().add(HelloFX.class.getResource("style.css").toExternalForm()); + + // SĂ€tt titel + stage.setTitle("Java Chat"); stage.setScene(scene); stage.show(); } @@ -21,5 +29,4 @@ public void start(Stage stage) throws Exception { public static void main(String[] args) { launch(); } - -} \ No newline at end of file +} From 0b4c16fe82d5f29605983c37fd00f10995238228 Mon Sep 17 00:00:00 2001 From: Eric Thilen Date: Fri, 14 Nov 2025 21:38:16 +0100 Subject: [PATCH 06/13] commit --- src/main/java/com/example/NtfyMessageDto.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/example/NtfyMessageDto.java b/src/main/java/com/example/NtfyMessageDto.java index 697cccac..7e865b17 100644 --- a/src/main/java/com/example/NtfyMessageDto.java +++ b/src/main/java/com/example/NtfyMessageDto.java @@ -3,5 +3,4 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) -public record NtfyMessageDto(String id, long time, String event, String topic, String message) { -} \ No newline at end of file +public record NtfyMessageDto(String event, String topic, String message) {} From 6caee10e42f2e4b53685c3b7c11513afce89ba16 Mon Sep 17 00:00:00 2001 From: Eric Thilen Date: Fri, 14 Nov 2025 21:39:24 +0100 Subject: [PATCH 07/13] commit --- .gitignore | 1 + mvnw | 0 2 files changed, 1 insertion(+) mode change 100644 => 100755 mvnw diff --git a/.gitignore b/.gitignore index 6ac465db..244268f9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ target/ /.idea/ +.env diff --git a/mvnw b/mvnw old mode 100644 new mode 100755 From 12c2d78be4f7bb60d8cd311813f9507b134ca515 Mon Sep 17 00:00:00 2001 From: Eric Thilen Date: Fri, 14 Nov 2025 21:39:37 +0100 Subject: [PATCH 08/13] Finished with the design in css --- src/main/resources/com/example/Style.css | 80 ++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 src/main/resources/com/example/Style.css diff --git a/src/main/resources/com/example/Style.css b/src/main/resources/com/example/Style.css new file mode 100644 index 00000000..781efea9 --- /dev/null +++ b/src/main/resources/com/example/Style.css @@ -0,0 +1,80 @@ +/* Global root styling */ +.root { + -fx-background-color: linear-gradient(to bottom, #e6f2ff, #cce0ff); /* mjuk ljusblĂ„ gradient */ + -fx-border-color: #ffffff; + -fx-border-width: 4; + -fx-border-radius: 12; + -fx-background-radius: 12; + -fx-font-family: "Segoe UI Emoji", "Segoe UI", sans-serif; + -fx-font-size: 14px; +} + +/* Titel högst upp */ +.app-title { + -fx-text-fill: #003366; /* mörkblĂ„ text */ + -fx-font-size: 28px; + -fx-font-weight: bold; + -fx-padding: 16; + -fx-alignment: center; + -fx-background-color: #ffffff; + -fx-background-radius: 0 0 12 12; + -fx-effect: dropshadow(gaussian, rgba(0,0,0,0.2), 6,0,0,2); +} + +/* Chat-omrĂ„det */ +.chat-area { + -fx-control-inner-background: #ffffff; + -fx-text-fill: #000000; + -fx-border-color: #99ccff; + -fx-border-radius: 8; + -fx-background-radius: 8; + -fx-padding: 12; + -fx-font-size: 14px; + -fx-effect: innershadow(gaussian, rgba(0,0,0,0.1), 4,0,0,2); +} + +/* InputfĂ€lt */ +.chat-input { + -fx-background-color: #ffffff; + -fx-text-fill: #000000; + -fx-border-color: #99ccff; + -fx-border-radius: 8; + -fx-background-radius: 8; + -fx-padding: 8; + -fx-font-size: 14px; +} + +/* Input-bar lĂ€ngst ner */ +.input-bar { + -fx-background-color: #f0f8ff; + -fx-background-radius: 8; + -fx-padding: 8; +} + +/* Skicka-knappen */ +.send-btn { + -fx-background-color: #4a90e2; + -fx-text-fill: #ffffff; + -fx-font-weight: bold; + -fx-background-radius: 8; + -fx-padding: 10 20; + -fx-cursor: hand; + -fx-font-size: 14px; +} +.send-btn:hover { + -fx-background-color: #003366; /* mörkblĂ„ vid hover */ +} + +/* Attach File-knappen */ +.attach-btn { + -fx-background-color: #50c9c3; + -fx-text-fill: #ffffff; + -fx-font-weight: bold; + -fx-background-radius: 8; + -fx-padding: 10 20; + -fx-cursor: hand; + -fx-font-size: 14px; +} +.attach-btn:hover { + -fx-background-color: #3aa7a0; /* mörkare turkos vid hover */ +} From 8c030c34186a6f0af284d76c2284d4b38aedbb65 Mon Sep 17 00:00:00 2001 From: Eric Thilen Date: Fri, 14 Nov 2025 21:39:47 +0100 Subject: [PATCH 09/13] commit --- pom.xml | 67 ++++++++++++++++++++++++--------------------------------- 1 file changed, 28 insertions(+), 39 deletions(-) diff --git a/pom.xml b/pom.xml index f410eea0..b8c4e603 100644 --- a/pom.xml +++ b/pom.xml @@ -4,81 +4,70 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - com.example - javafx + org.example + JavaFXChatApp 1.0-SNAPSHOT 25 UTF-8 - 6.0.0 - 3.27.6 - 5.20.0 - 25 + 5.13.4 + - - org.junit.jupiter - junit-jupiter - ${junit.jupiter.version} - test - - - org.assertj - assertj-core - ${assertj.core.version} - test - - - org.mockito - mockito-junit-jupiter - ${mockito.version} - test - + org.openjfx javafx-controls - ${javafx.version} + 25 org.openjfx javafx-fxml - ${javafx.version} + 25 + + io.github.cdimascio dotenv-java 3.2.0 + + - tools.jackson.core + com.fasterxml.jackson.core jackson-databind - 3.0.1 + 2.17.0 + + - org.wiremock - wiremock - 4.0.0-beta.15 + org.junit.jupiter + junit-jupiter + ${junit.jupiter.version} test + + org.openjfx javafx-maven-plugin 0.0.8 com.example.HelloFX - - - - javafx - true - true - true + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.2.5 + - \ No newline at end of file + From 9a41f791c5cdfb956ad7b2d0ec57751714603eea Mon Sep 17 00:00:00 2001 From: Eric Thilen Date: Fri, 14 Nov 2025 21:40:01 +0100 Subject: [PATCH 10/13] commit --- src/main/java/module-info.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 89e041cb..f2f67a0a 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,11 +1,11 @@ module hellofx { requires javafx.controls; requires javafx.fxml; - requires io.github.cdimascio.dotenv.java; requires java.net.http; - requires tools.jackson.databind; - requires javafx.graphics; + requires io.github.cdimascio.dotenv.java; + requires com.fasterxml.jackson.databind; + requires com.fasterxml.jackson.annotation; - opens com.example to javafx.fxml; + opens com.example to javafx.fxml, com.fasterxml.jackson.databind; exports com.example; -} \ No newline at end of file +} From 0b833633368523bf64e8f3ac8c57fdde9919d62d Mon Sep 17 00:00:00 2001 From: Eric Thilen Date: Fri, 14 Nov 2025 21:40:05 +0100 Subject: [PATCH 11/13] commit --- src/test/java/com/example/HelloModelTest.java | 47 +++++++++---------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/src/test/java/com/example/HelloModelTest.java b/src/test/java/com/example/HelloModelTest.java index a366e99d..36720844 100644 --- a/src/test/java/com/example/HelloModelTest.java +++ b/src/test/java/com/example/HelloModelTest.java @@ -1,37 +1,32 @@ package com.example; -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; -import static com.github.tomakehurst.wiremock.client.WireMock.*; -import static org.assertj.core.api.Assertions.assertThat; - -class HelloModelTest { +public class HelloModelTest { @Test - @DisplayName("Given a model with messageToSend when calling sendMessage then send method on connection should be called") - void sendMessageCallsConnectionWithMessageToSend() { - //Arrange Given - var spy = new NtfyConnectionSpy(); - var model = new HelloModel(spy); - model.setMessageToSend("Hello World"); - //Act When - model.sendMessage(); - //Assert Then - assertThat(spy.message).isEqualTo("Hello World"); + void testModelInitializationWithEnv() { + try { + HelloModel model = new HelloModel(); + assertNotNull(model, "Model should be created successfully with .env"); + } catch (IllegalStateException e) { + assertTrue(e.getMessage().contains("BACKEND_URL"), "Exception should mention BACKEND_URL"); + } } @Test - void sendMessageToFakeServer(WireMockRuntimeInfo wireMockRuntimeInfo) { - var con = new NtfyConnectionImpl("https://ntfy.fungover.org" + wmRuntimeInfo.getHttpPort()); - var model = new HelloModel(con); - model.setMessageToSend("Hello World"); - stubFor(post("/mytopic").willReturn(ok())) - - model.sendMessage(); + void testParseIncomingLineAddsEmoji() { + HelloModel model = new HelloModel("test-topic", "https://ntfy.sh"); + String input = "{\"message\":\"Hej\"}"; + String result = model.parseIncomingLine(input); + assertTrue(result.startsWith("💬 "), "Meddelandet ska börja med emoji"); + assertTrue(result.contains("Hej"), "Meddelandet ska innehĂ„lla originaltexten"); + } - verify(postRequestedFor(urlEqualTo("/mytopic")) - .withRequestBody(matching("Hello World"))); + @Test + void testSendMessageDoesNotThrow() { + HelloModel model = new HelloModel("test-topic", "https://ntfy.sh"); + assertDoesNotThrow(() -> model.sendMessage("Testmeddelande")); } -} \ No newline at end of file +} From 8d6c523f95308ede51a4121c927df6c20ed47018 Mon Sep 17 00:00:00 2001 From: Eric Thilen Date: Fri, 14 Nov 2025 21:40:07 +0100 Subject: [PATCH 12/13] commit --- .../resources/com/example/hello-view.fxml | 44 +++++++++++++++---- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/src/main/resources/com/example/hello-view.fxml b/src/main/resources/com/example/hello-view.fxml index 20a7dc82..db96804b 100644 --- a/src/main/resources/com/example/hello-view.fxml +++ b/src/main/resources/com/example/hello-view.fxml @@ -1,9 +1,37 @@ - - - - - - - + + + + + + + + + + + +
+