diff --git a/.gitignore b/.gitignore
index 6ac465db..244268f9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
target/
/.idea/
+.env
diff --git a/pom.xml b/pom.xml
index c40f667e..507a44b4 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
+
diff --git a/src/main/java/com/example/HelloController.java b/src/main/java/com/example/HelloController.java
index fdd160a0..5149dde8 100644
--- a/src/main/java/com/example/HelloController.java
+++ b/src/main/java/com/example/HelloController.java
@@ -1,22 +1,66 @@
package com.example;
+
import javafx.fxml.FXML;
import javafx.scene.control.Label;
+import javafx.scene.control.ListView;
+import javafx.scene.control.TextField;
+import javafx.stage.FileChooser;
+import javafx.stage.Stage;
+import java.io.File;
+import java.io.IOException;
-/**
- * Controller layer: mediates between the view (FXML) and the model.
- */
public class HelloController {
- private final HelloModel model = new HelloModel();
+ public final HelloModel model = new HelloModel(new NtfyConnectionImpl());
+ @FXML private ListView messageList;
+ @FXML private TextField inputField;
+ @FXML private Label fileLabel;
+
+ private File selectedFile;
+
@FXML
- private Label messageLabel;
+ public void initialize() {
+ messageList.setItems(model.getMessages());
+ }
+
+
+ public void sendMessage() {
+ String message = inputField.getText().trim();
+ if (!message.isEmpty()) {
+ model.setMessageToSend(message);
+ model.sendMessage();
+ inputField.clear();
+ }
+ if (selectedFile != null) {
+ try {
+ sendFileToServer(selectedFile);
+ fileLabel.setText("File sent successfully");
+ } catch (IOException e) {
+ fileLabel.setText("Error sending file");
+ }
+ } else {
+ fileLabel.setText("No file selected");
+ }
+ }
@FXML
- private void initialize() {
- if (messageLabel != null) {
- messageLabel.setText(model.getGreeting());
+ public void attachFile() {
+ FileChooser fileChooser = new FileChooser();
+ fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Files", "*.*"));
+ selectedFile = fileChooser.showOpenDialog(new Stage());
+ if (selectedFile != null) {
+ fileLabel.setText("Selected: " + selectedFile.getName());
+ } else {
+ fileLabel.setText("No file selected");
}
}
+
+
+ private void sendFileToServer(File file) throws IOException {
+ model.sendFileToServer(file);
+ }
+
+
}
diff --git a/src/main/java/com/example/HelloFX.java b/src/main/java/com/example/HelloFX.java
index 96bdc5ca..fb3889a9 100644
--- a/src/main/java/com/example/HelloFX.java
+++ b/src/main/java/com/example/HelloFX.java
@@ -1,5 +1,6 @@
package com.example;
+
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
@@ -10,15 +11,15 @@ public class HelloFX extends Application {
@Override
public void start(Stage stage) throws Exception {
- FXMLLoader fxmlLoader = new FXMLLoader(HelloFX.class.getResource("hello-view.fxml"));
+ FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("hello-view.fxml"));
Parent root = fxmlLoader.load();
- Scene scene = new Scene(root, 640, 480);
- stage.setTitle("Hello MVC");
+ Scene scene = new Scene(root);
+ stage.setTitle("Tittle goes here");
stage.setScene(scene);
stage.show();
}
- public static void main(String[] args) {
+ static void main() {
launch();
}
diff --git a/src/main/java/com/example/HelloModel.java b/src/main/java/com/example/HelloModel.java
index 385cfd10..41d7ff7b 100644
--- a/src/main/java/com/example/HelloModel.java
+++ b/src/main/java/com/example/HelloModel.java
@@ -1,15 +1,47 @@
package com.example;
-/**
- * Model layer: encapsulates application data and business logic.
- */
+import javafx.application.Platform;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+
+import java.io.File;
+import java.io.IOException;
+
+
public class HelloModel {
- /**
- * 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 + ".";
+ private final NtfyConnection connection;
+
+ private final ObservableList messages = FXCollections.observableArrayList();
+ private final StringProperty messageToSend = new SimpleStringProperty();
+
+ public HelloModel(NtfyConnection connection) {
+ this.connection = connection;
+ receiveMessage();
+ }
+
+
+ public ObservableList getMessages() {
+ return messages;
+ }
+
+ public void setMessageToSend(String message) {
+ this.messageToSend.set(message);
}
+
+
+ public void sendMessage() {
+ connection.send(messageToSend.get());
+ }
+
+ public void sendFileToServer(File file) throws IOException {
+ connection.sendFile(file.toPath());
+ }
+
+ public void receiveMessage() {
+ connection.recieve(m-> Platform.runLater(()-> messages.add(m.message())));
+ }
+
}
+
diff --git a/src/main/java/com/example/NtfyConnection.java b/src/main/java/com/example/NtfyConnection.java
new file mode 100644
index 00000000..263b75fb
--- /dev/null
+++ b/src/main/java/com/example/NtfyConnection.java
@@ -0,0 +1,14 @@
+package com.example;
+
+import java.io.FileNotFoundException;
+import java.nio.file.Path;
+import java.util.function.Consumer;
+
+public interface NtfyConnection {
+
+ boolean send(String message);
+
+ void recieve(Consumer messageHandler);
+
+ boolean sendFile(Path filePath) throws FileNotFoundException;
+}
diff --git a/src/main/java/com/example/NtfyConnectionImpl.java b/src/main/java/com/example/NtfyConnectionImpl.java
new file mode 100644
index 00000000..3cead2f4
--- /dev/null
+++ b/src/main/java/com/example/NtfyConnectionImpl.java
@@ -0,0 +1,82 @@
+package com.example;
+
+
+import io.github.cdimascio.dotenv.Dotenv;
+import tools.jackson.databind.ObjectMapper;
+import java.io.FileNotFoundException;
+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.nio.file.Path;
+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"));
+ }
+
+ public NtfyConnectionImpl(String hostName) {
+ this.hostName = hostName;
+ }
+
+ @Override
+ public boolean send(String message) {
+ HttpRequest httpRequest = HttpRequest.newBuilder()
+ .POST(HttpRequest.BodyPublishers.ofString(message))
+ .uri(URI.create(hostName + "/mytopic"))
+ .build();
+ try {
+ http.send(httpRequest, HttpResponse.BodyHandlers.ofString());
+ return true;
+ } catch (IOException e) {
+ System.out.println("IOException");
+ } catch (InterruptedException e){
+ System.out.println("Interrupted");
+ }
+ return false;
+ }
+
+
+ public boolean sendFile(Path filePath) throws FileNotFoundException {
+ HttpRequest httpRequest = HttpRequest.newBuilder()
+ .POST(HttpRequest.BodyPublishers.ofFile(filePath))
+ .uri(URI.create(hostName + "/mytopic"))
+ .build();
+
+ try {
+ http.send(httpRequest, HttpResponse.BodyHandlers.ofFile(filePath));
+ return true;
+ } catch (IOException e) {
+ System.out.println("IOException");
+ } catch (InterruptedException e){
+ System.out.println("Interrupted");
+ }
+ return false;
+ }
+
+ @Override
+ public void recieve(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"))
+ .forEach(messageHandler));
+
+
+ }
+}
diff --git a/src/main/java/com/example/NtfyMessageDto.java b/src/main/java/com/example/NtfyMessageDto.java
new file mode 100644
index 00000000..c6e36d69
--- /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){
+}
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index 71574a27..23f3419e 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -1,6 +1,11 @@
module hellofx {
requires javafx.controls;
requires javafx.fxml;
+ requires java.desktop;
+ 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/main/resources/com/example/hello-view.fxml b/src/main/resources/com/example/hello-view.fxml
index 20a7dc82..b03c01a3 100644
--- a/src/main/resources/com/example/hello-view.fxml
+++ b/src/main/resources/com/example/hello-view.fxml
@@ -1,9 +1,20 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/css/style.css b/src/main/resources/css/style.css
new file mode 100644
index 00000000..b5562a84
--- /dev/null
+++ b/src/main/resources/css/style.css
@@ -0,0 +1,25 @@
+/*.chatpan{*/
+/* -fx-background-color: #AD1A4D;*/
+/*}*/
+
+/*.chat-box{*/
+/* -fx-background-color: #BA2200;*/
+/* -fx-min-height: 400px;*/
+/* -fx-min-width: 200px;*/
+/*}*/
+
+/*.label{*/
+/* -fx-background-color: #FC7400;*/
+/* -fx-text-fill: white;*/
+/* -fx-pref-height: 20px;*/
+/* -fx-pref-width: 200px;*/
+/*}*/
+
+/*.button{*/
+/* -fx-background-color: #FC7400;*/
+/* -fx-text-fill: white;*/
+/* !*-fx-pref-height: 25px;*/
+/* -fx-pref-width: 50px;*/
+/* !*-fx-translate-x: 75;*/
+/* -fx-translate-y: 410;*!*/
+/*}*/
\ No newline at end of file
diff --git a/src/test/java/com/example/HelloModelTest.java b/src/test/java/com/example/HelloModelTest.java
new file mode 100644
index 00000000..c4503d78
--- /dev/null
+++ b/src/test/java/com/example/HelloModelTest.java
@@ -0,0 +1,48 @@
+package com.example;
+
+
+import com.github.tomakehurst.wiremock.WireMockServer;
+import com.github.tomakehurst.wiremock.client.WireMock;
+import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo;
+import com.github.tomakehurst.wiremock.junit5.WireMockTest;
+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;
+
+@WireMockTest
+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();
+
+ //Asser Then
+ assertThat(spy.message).isEqualTo("Hello world!");
+
+ }
+
+ @Test
+ void sendMessageToFakeServer(WireMockRuntimeInfo wmRuntimeInfo) {
+ var con = new NtfyConnectionImpl("http://localhost:" + wmRuntimeInfo.getHttpPort());
+ var model = new HelloModel(con);
+ model.setMessageToSend("Hello world");
+ stubFor(post("/mytopic").willReturn(ok()));
+
+ model.sendMessage();
+
+ //Verify call made to server
+ verify(postRequestedFor(urlEqualTo("/mytopic"))
+ .withRequestBody(containing("Hello world")));
+
+
+ }
+
+}
\ 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..c4fb031c
--- /dev/null
+++ b/src/test/java/com/example/NtfyConnectionSpy.java
@@ -0,0 +1,26 @@
+package com.example;
+
+import java.io.FileNotFoundException;
+import java.nio.file.Path;
+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 recieve(Consumer messageHandler) {
+
+ }
+
+ @Override
+ public boolean sendFile(Path filePath) throws FileNotFoundException {
+ return false;
+ }
+}