diff --git a/pom.xml b/pom.xml index c40f667e..fdb520a9 100644 --- a/pom.xml +++ b/pom.xml @@ -45,9 +45,23 @@ javafx-fxml ${javafx.version} + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + 25 + 25 + 25 + + + org.openjfx javafx-maven-plugin @@ -55,7 +69,7 @@ com.example.HelloFX - + javafx true @@ -63,6 +77,22 @@ true + + + org.apache.maven.plugins + maven-surefire-plugin + 3.1.2 + + false + + **/*Test.java + + + + + + + diff --git a/src/main/java/com/example/ChatController.java b/src/main/java/com/example/ChatController.java new file mode 100644 index 00000000..2cc49f68 --- /dev/null +++ b/src/main/java/com/example/ChatController.java @@ -0,0 +1,42 @@ +package com.example; + +import javafx.fxml.FXML; +import javafx.scene.control.ListView; +import javafx.scene.control.TextField; +import javafx.application.Platform; + +public class ChatController { + + @FXML + private ListView messagesList; + + @FXML + private TextField inputField; + + private final ChatModel model = new ChatModel(); + + + @FXML + private void onSend() { + String message = inputField.getText().trim(); + if (!message.isEmpty()) { + messagesList.getItems().add("Me: " + message); + model.sendMessage(message); + inputField.clear(); + } + } + + + @FXML + private void initialize(){ + model.subscribe(msg -> { + Platform.runLater(() -> messagesList.getItems().add("Friend: " + msg)); + }); + } + + + + + + +} diff --git a/src/main/java/com/example/ChatModel.java b/src/main/java/com/example/ChatModel.java new file mode 100644 index 00000000..dc5973f9 --- /dev/null +++ b/src/main/java/com/example/ChatModel.java @@ -0,0 +1,120 @@ +package com.example; + +import javafx.application.Platform; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.Collections; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ChatModel { + + private final String sendUrl; + private final String subscribeUrl; + private final String clientId; + private final Set sentMessages = Collections.newSetFromMap(new ConcurrentHashMap<>()); + private final HttpClient httpClient; + private final Consumer platformRunner; + + public ChatModel() { + this(HttpClient.newHttpClient(), Platform::runLater); + } + + public ChatModel(HttpClient httpClient, Consumer platformRunner) { + String topic = System.getenv().getOrDefault("NTFY_TOPIC", "https://ntfy.sh/newchatroom3"); + this.sendUrl = topic; + this.subscribeUrl = topic + "/sse"; + this.clientId = UUID.randomUUID().toString(); + this.httpClient = httpClient; + this.platformRunner = platformRunner; + } + + public void sendMessage(String message) { + sentMessages.add(message); + + new Thread(() -> { + try { + Thread.sleep(5000); + } catch (InterruptedException ignored) { + } + sentMessages.remove(message); + }, "SentMessageCleaner").start(); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(sendUrl)) + .header("Content-Type", "application/json") + .header("X-Client-ID", clientId) + .header("Title", "Friend: ") + .POST(HttpRequest.BodyPublishers.ofString(message)) + .build(); + + httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenAccept(response -> System.out.println("Sent, status=" + response.statusCode())) + .exceptionally(e -> { + e.printStackTrace(); + return null; + }); + } + + + public void subscribe(Consumer onMessageReceived) { + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(subscribeUrl)) + .header("Accept", "text/event-stream") + .build(); + + httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream()) + .thenAccept(response -> { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(response.body()))) { + String line; +// boolean firstMessageSkipped = false; + while ((line = reader.readLine()) != null) { + if (line.startsWith("data:")) { +// if (!firstMessageSkipped) { +// firstMessageSkipped = true; +// continue; +// } + + String raw = line.substring(5).trim(); + String msg = parseMessage(raw); + + if (msg != null && !sentMessages.contains(msg)) { + platformRunner.accept(() -> onMessageReceived.accept(msg)); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + }) + .exceptionally(e -> { e.printStackTrace(); return null; }); + } + + public String parseMessage(String data) { + try { + Matcher eventMatcher = Pattern.compile("\"event\"\\s*:\\s*\"(.*?)\"").matcher(data); + if (eventMatcher.find()) { + String event = eventMatcher.group(1); + if (!"message".equals(event)) { + return null; + } + } + + Matcher msgMatcher = Pattern.compile("\"message\"\\s*:\\s*\"(.*?)\"").matcher(data); + if (msgMatcher.find()) { + return msgMatcher.group(1).replace("\\\"", "\""); + } + } catch (Exception ignored) {} + return null; + } + +} diff --git a/src/main/java/com/example/HelloFX.java b/src/main/java/com/example/HelloFX.java index 96bdc5ca..be82aace 100644 --- a/src/main/java/com/example/HelloFX.java +++ b/src/main/java/com/example/HelloFX.java @@ -10,10 +10,10 @@ 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(HelloFX.class.getResource("chat-view.fxml")); Parent root = fxmlLoader.load(); Scene scene = new Scene(root, 640, 480); - stage.setTitle("Hello MVC"); + stage.setTitle("Chat App"); stage.setScene(scene); stage.show(); } diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 71574a27..0a19298c 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,6 +1,8 @@ module hellofx { requires javafx.controls; requires javafx.fxml; + requires java.net.http; + opens com.example to javafx.fxml; exports com.example; diff --git a/src/main/resources/com/example/chat-view.fxml b/src/main/resources/com/example/chat-view.fxml new file mode 100644 index 00000000..667d541b --- /dev/null +++ b/src/main/resources/com/example/chat-view.fxml @@ -0,0 +1,24 @@ + + + + + + + + + + +
+ +
+ + + + +