Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
target/
/.idea/
.env
15 changes: 15 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,21 @@
<artifactId>javafx-fxml</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>io.github.cdimascio</groupId>
<artifactId>dotenv-java</artifactId>
<version>3.2.0</version>
</dependency><dependency>
<groupId>tools.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>org.wiremock</groupId>
<artifactId>wiremock</artifactId>
<version>4.0.0-beta.15</version>
<scope>test</scope>
</dependency>
Comment on lines +57 to +62
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

Verify WireMock stable release availability.

The dependency uses a beta version (4.0.0-beta.15). Consider using a stable release for better reliability in tests.


🌐 Web query:

What is the latest stable version of WireMock for Java?

💡 Result:

The latest stable WireMock for Java is version 3.13.1 (released June 12, 2025). [1][2]

Note: WireMock 4.x is currently available only as beta releases. [2]

Sources:

  • WireMock community announcement (3.13.1). [1]
  • WireMock docs (v4 beta info). [2]

Consider upgrading to the latest stable WireMock version.

The latest stable WireMock for Java is version 3.13.1, while the current dependency uses 4.0.0-beta.15. WireMock 4.x is currently available only as beta releases. Update pom.xml line 59 to use version 3.13.1 for improved stability in your test suite.

🤖 Prompt for AI Agents
In pom.xml around lines 57 to 62 (specifically line 59), the WireMock dependency
is set to a 4.x beta version; change the <version> value from 4.0.0-beta.15 to
the stable 3.13.1 to improve test stability, keeping the groupId, artifactId and
scope unchanged.

</dependencies>
<build>
<plugins>
Expand Down
147 changes: 136 additions & 11 deletions src/main/java/com/example/HelloController.java
Original file line number Diff line number Diff line change
@@ -1,22 +1,147 @@
package com.example;

import javafx.application.Platform;
import javafx.collections.ListChangeListener;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.HBox;
import javafx.stage.FileChooser;

import javax.swing.event.HyperlinkListener;
import javax.tools.Tool;
import java.awt.Desktop;
import java.io.File;
import java.io.FileNotFoundException;
import java.net.URI;
import java.nio.file.Path;
import java.time.format.DateTimeFormatter;
import java.util.List;

/**
* 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(new NtfyConnectionImpl());

private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm");

@FXML private ListView<NtfyMessageDto> messageView;
@FXML private TextArea messageInput;

@FXML private void initialize() {
messageView.setItems(model.getMessages());
messageView.setCellFactory(lv -> new ListCell<>() {
@Override
protected void updateItem(NtfyMessageDto item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
return;
}
String text = item.message();
var att = item.attachment();

if (att != null && att.url() != null && !att.url().isBlank()) {
Label msg = new Label(text != null ? text + " ": "");
msg.setWrapText(true);

String linkText = att.name() != null && !att.name().isBlank()
? att.name()
: "Attachment";
Hyperlink link = new Hyperlink(linkText);
if (att.size() > 0) {
link.setTooltip(new Tooltip(humanSize(att.size()) + (att.type() != null ? " - " + att.type() : "")));
}
Comment on lines +38 to +58
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Fix potential NPE when attachment size is null

Attachment.size is a Long, but it’s used as a primitive:

if (att.size() > 0) {
    link.setTooltip(new Tooltip(humanSize(att.size()) + (att.type() != null ? " - " + att.type() : "")));
}

If size is absent in the JSON, this will throw a NullPointerException.

Consider guarding the access:

-                    if (att.size() > 0) {
-                        link.setTooltip(new Tooltip(humanSize(att.size()) + (att.type() != null ? " - " + att.type() : "")));
-                    }
+                    if (att.size() != null && att.size() > 0) {
+                        link.setTooltip(new Tooltip(
+                                humanSize(att.size()) +
+                                        (att.type() != null ? " - " + att.type() : "")
+                        ));
+                    }

This keeps the tooltip for known sizes but safely handles missing size.



---

🏁 Script executed:

```shell
#!/bin/bash
# Find Attachment class definition
fd -t f -e java | xargs rg -l "class Attachment|record Attachment" -A5

Length of output: 45


🏁 Script executed:

#!/bin/bash
# Search for Attachment.java file directly
fd -t f "Attachment.java"

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Find NtfyMessageDto class and understand attachment() return type
fd -t f -e java | xargs rg -nP "NtfyMessageDto|record NtfyMessageDto|class NtfyMessageDto" -A10

Length of output: 9331


🏁 Script executed:

#!/bin/bash
# Search for humanSize method definition
fd -t f -e java | xargs rg -nP "humanSize\s*\(" -B2 -A5

Length of output: 1496


🏁 Script executed:

#!/bin/bash
# Check HelloController.java lines around 38-58 for full context
fd -t f "HelloController.java" | xargs cat

Length of output: 5091



---

🏁 Script executed:

```shell
#!/bin/bash
# Get complete NtfyMessageDto.java to see full Attachment record definition
cat src/main/java/com/example/NtfyMessageDto.java

Length of output: 509


Fix potential NPE when attachment size is null

Attachment.size is a boxed Long, but line 56 uses it as a primitive without null-checking:

if (att.size() > 0) {
    link.setTooltip(new Tooltip(humanSize(att.size()) + (att.type() != null ? " - " + att.type() : "")));
}

When size is absent in the JSON, unboxing a null Long throws NullPointerException.

Apply the guard:

-                    if (att.size() > 0) {
-                        link.setTooltip(new Tooltip(humanSize(att.size()) + (att.type() != null ? " - " + att.type() : "")));
-                    }
+                    if (att.size() != null && att.size() > 0) {
+                        link.setTooltip(new Tooltip(
+                                humanSize(att.size()) +
+                                        (att.type() != null ? " - " + att.type() : "")
+                        ));
+                    }
🤖 Prompt for AI Agents
In src/main/java/com/example/HelloController.java around lines 38 to 58, the
code unboxes Attachment.size() (a Long) without null-checking which can throw an
NPE; change the condition to first check for null (e.g. att.size() != null &&
att.size() > 0) and only build/call humanSize(att.size()) when size is non-null,
keeping the existing type() null-safe logic for the tooltip.

link.setOnAction(e -> openInBrowser(att.url()));

@FXML
private Label messageLabel;
HBox row = new HBox(8.0, (Node) msg, (Node) link);
row.setFillHeight(true);

setText(null);
setGraphic(row);
} else {
setText(text != null ? text : "");
setGraphic(null);
}
}
});

model.getMessages().addListener((ListChangeListener<NtfyMessageDto>)
c -> Platform.runLater(() -> {
if (!messageView.getItems(). isEmpty()) {
messageView.scrollTo(messageView.getItems().size() - 1);
}
})
);
messageView.setOnDragOver(this::handleDragOver);
messageView.setOnDragDropped(this::handleDragDropped);
model.loadInitialMessagesAsync();
}

@FXML public void sendFile(ActionEvent actionEvent) throws FileNotFoundException {
FileChooser chooser = new FileChooser();
chooser.setTitle("Välj fil att skicka");
File file = chooser.showOpenDialog(messageView.getScene().getWindow());
if (file != null) {
Path path = file.toPath();
model.sendFile(path);
}
}
Comment on lines +85 to +93
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

Handle file-sending errors in the UI instead of propagating/crashing

Two related places:

  • sendFile(ActionEvent) currently declares throws FileNotFoundException and doesn’t catch anything.
  • handleDragDropped catches FileNotFoundException and rethrows a RuntimeException.

Both can surface as unhandled exceptions on the JavaFX application thread if the file disappears or is unreadable between selection and send.

Consider:

  • Handling FileNotFoundException inside the controller and giving user feedback (e.g. an Alert or status message).
  • Avoiding rethrowing as RuntimeException inside the drag handler; instead, log and continue, or show a one-time error.

For example:

-    @FXML public void sendFile(ActionEvent actionEvent) throws FileNotFoundException {
+    @FXML public void sendFile(ActionEvent actionEvent) {
         FileChooser chooser = new FileChooser();
         chooser.setTitle("Välj fil att skicka");
         File file = chooser.showOpenDialog(messageView.getScene().getWindow());
         if (file != null) {
             Path path = file.toPath();
-            model.sendFile(path);
+            try {
+                model.sendFile(path);
+            } catch (FileNotFoundException e) {
+                // TODO: show an error dialog / status label so the user knows the file was not found
+            }
         }
     }
         if (db.hasFiles()) {
             List<File> files = db.getFiles();
             for (File f : files) {
-                try {
-                    model.sendFile(f.toPath());
-                } catch (FileNotFoundException ex) {
-                    throw new RuntimeException(ex);
-                }
+                try {
+                    model.sendFile(f.toPath());
+                } catch (FileNotFoundException ex) {
+                    // TODO: log and/or notify the user for this file, but don’t crash the UI
+                }
             }
             success = true;
         }

This keeps the UX resilient while still surfacing problems to the user.

Also applies to: 110-121


🏁 Script executed:

#!/bin/bash
# Find HelloController.java and examine the two methods in question
fd -t f "HelloController.java" --exec cat {} \;

Length of output: 5091


🏁 Script executed:

#!/bin/bash
# Alternative: search for the sendFile method and handleDragDropped in Java files
rg -n "sendFile|handleDragDropped" --type java -A 10 -B 2

Length of output: 5801


🏁 Script executed:

#!/bin/bash
# Look for FileNotFoundException handling patterns in the codebase
rg -n "FileNotFoundException" --type java -C 3

Length of output: 4810


🏁 Script executed:

#!/bin/bash
# Find the file at the path specified in the review
fd -t f "src/main/java/com/example/HelloController.java"

Length of output: 0


🏁 Script executed:

#!/bin/bash
# If file exists, extract lines 85-93 and 110-121
if [ -f "src/main/java/com/example/HelloController.java" ]; then
  echo "=== Lines 85-93 (sendFile method) ===";
  sed -n '85,93p' "src/main/java/com/example/HelloController.java";
  echo "";
  echo "=== Lines 110-121 (handleDragDropped method) ===";
  sed -n '110,121p' "src/main/java/com/example/HelloController.java";
fi

Length of output: 910


🏁 Script executed:

#!/bin/bash
# Check entire controller file if found to understand context
fd -t f "HelloController.java" -x wc -l {}

Length of output: 52


🏁 Script executed:

#!/bin/bash
# Search for all throws declarations in Java files
rg -n "throws FileNotFoundException" --type java -C 2

Length of output: 1502


🏁 Script executed:

#!/bin/bash
# Search for RuntimeException rethrows in Java files
rg -n "throw new RuntimeException" --type java -B 3 -A 1

Length of output: 447


🏁 Script executed:

#!/bin/bash
# Look for javafx FileChooser usage
rg -n "FileChooser\|showOpenDialog" --type java -B 2 -A 5

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Search for model.sendFile calls
rg -n "model\.sendFile" --type java -B 2 -A 2

Length of output: 834


🏁 Script executed:

#!/bin/bash
# Check for dragDropped handling with model.sendFile
rg -n "dragDropped\|handleDragDropped" --type java -B 3 -A 15

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Look for Alert usage (as suggested in review for user feedback)
rg -n "Alert\|showError\|showWarning" --type java

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Find model class and sendFile method signature
rg -n "sendFile\s*\(" --type java -B 1 -A 3

Length of output: 2408


🏁 Script executed:

#!/bin/bash
# Search for JavaFX application thread context issues
rg -n "Platform\.runLater\|FXMLLoader\|@FXML" --type java | head -20

Length of output: 0


🏁 Script executed:

#!/bin/bash
# List all files in the source directory
find src/main/java -type f -name "*.java" 2>/dev/null | head -20

Length of output: 300


🏁 Script executed:

#!/bin/bash
# Get a count of Java files
find . -type f -name "*.java" 2>/dev/null | wc -l

Length of output: 1


🏁 Script executed:

#!/bin/bash
# If the main file exists, get its full content
find . -type f -name "HelloController.java" 2>/dev/null -exec echo "File found at: {}" \; -exec cat {} \;

Length of output: 5155


🏁 Script executed:

#!/bin/bash
# Try to locate the file regardless of case
find . -iname "*hellocontroller*" -type f 2>/dev/null

Length of output: 48


🏁 Script executed:

#!/bin/bash
# Search for any Java file with "Hello" in the name
find . -type f -name "*Hello*.java" 2>/dev/null

Length of output: 181


🏁 Script executed:

#!/bin/bash
# Check if src directory exists and list structure
[ -d "src" ] && find src -type f -name "*.java" 2>/dev/null | head -20

Length of output: 395


🏁 Script executed:

#!/bin/bash
# Look for any controller files
find . -type f -name "*Controller.java" 2>/dev/null | head -20

Length of output: 48


🏁 Script executed:

#!/bin/bash
# Search for FXML annotations and file chooser patterns
rg -n "@FXML|FileChooser|showOpenDialog" --type java -l

Length of output: 46


🏁 Script executed:

#!/bin/bash
# Get detailed info from any matching Java file
rg -n "@FXML public void sendFile" --type java -A 15

Length of output: 1436


🏁 Script executed:

#!/bin/bash
# Search for the exact method signature from the review
rg -n "public void sendFile\(ActionEvent" --type java -A 10

Length of output: 987


🏁 Script executed:

#!/bin/bash
# Try to find any references to the methods mentioned
rg -n "void sendFile|void handleDragDropped" --type java -C 5

Length of output: 1940


🏁 Script executed:

#!/bin/bash
# Broader search for file operations and exceptions
rg -n "FileNotFoundException|model\.sendFile|drag.*drop" --type java -i

Length of output: 1332


🏁 Script executed:

#!/bin/bash
# Check Java files for exception handling patterns
rg -n "catch.*FileNotFoundException|throws.*FileNotFoundException" --type java -B 2 -A 3

Length of output: 2306


🏁 Script executed:

#!/bin/bash
# Final attempt: list all directories to understand structure
ls -la

Length of output: 689


🏁 Script executed:

#!/bin/bash
# Check if there's a pom.xml or build configuration
find . -maxdepth 3 -name "pom.xml" -o -name "build.gradle" -o -name "*.xml" | head -10

Length of output: 9


🏁 Script executed:

#!/bin/bash
# Look for example or demo Java applications
find . -type f -name "*.java" 2>/dev/null | xargs grep -l "HelloController\|sendFile\|FileChooser" 2>/dev/null

Length of output: 192


🏁 Script executed:

#!/bin/bash
# Get overall repository structure
find . -maxdepth 3 -type d 2>/dev/null | sort

Length of output: 313


🏁 Script executed:

#!/bin/bash
# Final comprehensive search for Java file patterns mentioned in review
find . -type f \( -name "*.java" -o -name "*.fxml" \) 2>/dev/null | xargs grep -l "sendFile\|handleDragDropped" 2>/dev/null || echo "No matches found"

Length of output: 241


🏁 Script executed:

#!/bin/bash
# Check README or documentation
find . -maxdepth 2 -name "README*" -o -name "readme*" 2>/dev/null | xargs cat 2>/dev/null | head -30

Length of output: 611


Handle file-sending errors in the controller to prevent UI crashes

Both file-sending entry points lack proper error handling:

  • sendFile(ActionEvent) at line 85 declares throws FileNotFoundException but doesn't catch it. The exception propagates uncaught on the JavaFX thread, crashing the application.
  • handleDragDropped(DragEvent) at line 118 catches FileNotFoundException and rethrows as RuntimeException, which also crashes the UI thread.

In both cases, a file can legitimately disappear or become unreadable between user selection/drag and the actual send operation.

Wrap model.sendFile() calls in try-catch and provide user feedback (e.g., Alert) instead of propagating or rethrowing:

src/main/java/com/example/HelloController.java, line 85:

  • Remove throws FileNotFoundException from the method signature
  • Wrap the model.sendFile(path) call in a try-catch block
  • Show an error alert to the user on FileNotFoundException

src/main/java/com/example/HelloController.java, lines 116–120:

  • Catch FileNotFoundException but log the error and continue processing remaining files instead of throwing RuntimeException
  • Optionally show a summary notification to the user if any files fail
🤖 Prompt for AI Agents
In src/main/java/com/example/HelloController.java around lines 85–93 and
116–120, remove the unchecked propagation of FileNotFoundException: delete the
"throws FileNotFoundException" from sendFile(ActionEvent), wrap the
model.sendFile(path) call in a try-catch that catches FileNotFoundException, log
the error and show a JavaFX Alert (ERROR) to the user instead of allowing the
exception to bubble up; in the drag-drop handler (around lines 116–120) catch
FileNotFoundException per file, log the failure and continue processing
remaining files (do not rethrow RuntimeException), optionally accumulate failed
filenames and display a single Alert summary after processing completes so the
UI does not crash.


@FXML
private void initialize() {
if (messageLabel != null) {
messageLabel.setText(model.getGreeting());
public void sendMessage(ActionEvent actionEvent) {
String text = messageInput != null ? messageInput.getText() : "";
if (text != null && !text.isBlank() && model.sendMessage(text)) {
messageInput.clear();
}
}

private void handleDragOver(DragEvent e) {
Dragboard db = e.getDragboard();
if (db.hasFiles()) {
e.acceptTransferModes(TransferMode.COPY);
}
e.consume();
}

private void handleDragDropped(DragEvent e) {
Dragboard db = e.getDragboard();
boolean success = false;
if (db.hasFiles()) {
List<File> files = db.getFiles();
for (File f : files) {
try {
model.sendFile(f.toPath());
} catch (FileNotFoundException ex) {
throw new RuntimeException(ex);
}
}
success = true;
}
e.setDropCompleted(success);
e.consume();
}

private void openInBrowser(String url) {
try {
if (Desktop.isDesktopSupported()) {
Desktop.getDesktop().browse(URI.create(url));
} else {
// fallback: ingen Desktop, gör inget (kan ersättas med HostServices)
}
} catch (Exception ignored) {
}
}

private static String humanSize(long bytes) {
// kort & enkel
String[] units = {"B","KB","MB","GB","TB"};
double v = bytes;
int i = 0;
while (v >= 1024 && i < units.length - 1) { v /= 1024; i++; }
return String.format("%.1f %s", v, units[i]);
}
}
1 change: 1 addition & 0 deletions src/main/java/com/example/HelloFX.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public class HelloFX extends Application {

@Override
public void start(Stage stage) throws Exception {

FXMLLoader fxmlLoader = new FXMLLoader(HelloFX.class.getResource("hello-view.fxml"));
Parent root = fxmlLoader.load();
Scene scene = new Scene(root, 640, 480);
Expand Down
70 changes: 60 additions & 10 deletions src/main/java/com/example/HelloModel.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,65 @@
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.FileNotFoundException;
import java.nio.file.Path;
import java.util.concurrent.CompletableFuture;

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<NtfyMessageDto> messages = FXCollections.observableArrayList();
//private final StringProperty messageToSend = new SimpleStringProperty();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Remove commented code.

Lines 16, 26-28, and 44-50 contain commented-out code that should be deleted rather than left in the source. If this code is needed for tests (as suggested by HelloModelTest.java), it should be restored; otherwise, remove it entirely.

Apply this diff if the code is truly unused:

     private final NtfyConnection connection;
     private final ObservableList<NtfyMessageDto> messages = FXCollections.observableArrayList();
-    //private final StringProperty messageToSend = new SimpleStringProperty();

     public HelloModel(NtfyConnection connection) {

And remove the commented sections at lines 26-28 and 44-50 as well.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
//private final StringProperty messageToSend = new SimpleStringProperty();
🤖 Prompt for AI Agents
In src/main/java/com/example/HelloModel.java around lines 16, 26-28 and 44-50
there are commented-out code blocks that should not remain in source; either
delete those commented lines entirely or, if tests require them, restore the
original code (uncomment and ensure the backing property and accessor methods
compile and match HelloModelTest.java). Remove the commented property at line 16
and the commented methods/blocks at 26-28 and 44-50, or reinstate them exactly
as needed by tests and run the test suite to confirm compilation.


public HelloModel(NtfyConnection connection) {
this.connection = connection;
}

public ObservableList<NtfyMessageDto> getMessages() {
return messages;
}

/*public String getMessageToSend() {
return messageToSend.get();
}*/

public void loadInitialMessagesAsync() {
CompletableFuture
.supplyAsync(connection::fetchHistory)
.thenAccept(list -> Platform.runLater(() -> {
messages.setAll(list);
subscribeLive(); // start streaming after history
}))
.exceptionally(ex -> null);
}
Comment on lines +30 to +38
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add error logging for async failures.

The exception handler silently discards errors (.exceptionally(ex -> null)), making debugging difficult when message loading fails. Consider logging the exception or updating UI state to indicate the failure.

Apply this diff to add basic error handling:

     public void loadInitialMessagesAsync() {
         CompletableFuture
                 .supplyAsync(connection::fetchHistory)
                 .thenAccept(list -> Platform.runLater(() -> {
                     messages.setAll(list);
                     subscribeLive(); // start streaming after history
                 }))
-                .exceptionally(ex -> null);
+                .exceptionally(ex -> {
+                    System.err.println("Failed to load message history: " + ex.getMessage());
+                    ex.printStackTrace();
+                    return null;
+                });
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public void loadInitialMessagesAsync() {
CompletableFuture
.supplyAsync(connection::fetchHistory)
.thenAccept(list -> Platform.runLater(() -> {
messages.setAll(list);
subscribeLive(); // start streaming after history
}))
.exceptionally(ex -> null);
}
public void loadInitialMessagesAsync() {
CompletableFuture
.supplyAsync(connection::fetchHistory)
.thenAccept(list -> Platform.runLater(() -> {
messages.setAll(list);
subscribeLive(); // start streaming after history
}))
.exceptionally(ex -> {
System.err.println("Failed to load message history: " + ex.getMessage());
ex.printStackTrace();
return null;
});
}
🤖 Prompt for AI Agents
In src/main/java/com/example/HelloModel.java around lines 30 to 38, the
CompletableFuture chain currently swallows exceptions with .exceptionally(ex ->
null); change it to handle and log the exception and, if appropriate, update UI
state on the JavaFX thread: in the .exceptionally block capture the Throwable,
call your logger (e.g., logger.error or Logger.getLogger(...).log) with a clear
message and the exception, and wrap any UI updates (like setting an error flag
or showing an error message in the model) inside Platform.runLater so the UI is
updated safely; ensure the exceptionally returns null (or the expected fallback)
after logging.


private void subscribeLive() {
connection.receive(m -> Platform.runLater(() -> messages.add(m)));
}

/*public StringProperty messageToSendProperty() {
return messageToSend;
}

public void setMessageToSend(String message) {
messageToSend.set(message);
}*/

public boolean sendMessage(String text) {
return connection.send(text);
}

public boolean sendFile(Path path) throws FileNotFoundException {
return connection.sendFile(path);
}

public void receiveMessage() {
connection.receive(m -> Platform.runLater(() -> messages.add(m)));
}


}
17 changes: 17 additions & 0 deletions src/main/java/com/example/NtfyConnection.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.example;

import java.io.FileNotFoundException;
import java.nio.file.Path;
import java.util.List;
import java.util.function.Consumer;

public interface NtfyConnection {

boolean send(String message);

void receive(Consumer<NtfyMessageDto> messageHandler);

List<NtfyMessageDto> fetchHistory();

boolean sendFile(Path path) throws FileNotFoundException;
}
Loading
Loading