Conversation
WalkthroughAdds a JavaFX chat feature: new ChatController and chat-view.fxml UI, a ChatModel implementing HTTP POST and SSE subscribe logic, switches the app to load the new FXML, adds java.net.http module requirement, and sets Maven compiler to Java 25. Changes
Sequence DiagramsequenceDiagram
participant User
participant UI as ChatController\n(JavaFX)
participant Model as ChatModel\n(HTTP POST / SSE)
User->>UI: Click Send (onSend)
UI->>UI: read & trim inputField
alt non-empty
UI->>UI: append "Me: " + message
UI->>Model: sendMessage(message)
Model->>Model: record message in sent set
Model->>Model: async HTTP POST → sendUrl
Note right of Model: async SSE subscribe reads events\nfilters local messages and parses payload
Model->>UI: Platform.runLater -> deliver "Friend: " + msg
UI->>UI: clear inputField
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Tip 📝 Customizable high-level summaries are now available!You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.
Example:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
pom.xml (1)
14-14: Update JUnit Jupiter version to a valid released version.The specified version 6.0.0 does not exist. The latest released version of JUnit Jupiter is 5.13.4. Update
junit.jupiter.versionin pom.xml (line 14) to a valid version from the 5.x series, such as 5.13.4.
🧹 Nitpick comments (1)
src/main/java/com/example/ChatModel.java (1)
6-6: Reminder: Address the TODO comment.The TODO indicates that the network functionality to send POST JSON to ntfy is not yet implemented.
Would you like me to help generate the HTTP client code for sending messages to ntfy, or should I open a new issue to track this task?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
pom.xml(1 hunks)src/main/java/com/example/ChatController.java(1 hunks)src/main/java/com/example/ChatModel.java(1 hunks)src/main/java/com/example/HelloFX.java(1 hunks)src/main/resources/com/example/chat-view.fxml(1 hunks)
🔇 Additional comments (4)
src/main/java/com/example/HelloFX.java (1)
12-19: LGTM!The FXML loading logic is correct and properly integrated with the new chat-view.fxml.
src/main/resources/com/example/chat-view.fxml (1)
1-24: LGTM!The FXML layout is well-structured with proper bindings to the ChatController. The UI components (ListView, TextField, Button) are correctly configured with fx:id attributes that match the controller's @FXML fields.
pom.xml (2)
17-17: JavaFX 25 is available and verified as the latest stable release.JavaFX 25 (latest patch 25.0.1) was released on October 21, 2025, confirming that the version specified in pom.xml is valid and current.
12-12: I need to verify Maven compiler plugin and JavaFX support for Java 25:I need to verify the Maven compiler plugin version specified in the pom.xml to confirm it supports Java 25:
All Java 25 availability and support concerns are resolved.
Java 25 (JDK) was released on September 16, 2025 and is available for use. Maven compiler plugin version 3.14.1 supports Java 25, and JavaFX 25 is designed to work with JDK 25 and is recommended for use with the GA release of JDK 25. The pom.xml configuration is valid and supported.
| @FXML | ||
| private TextField inputField; | ||
|
|
||
| private final ChatModel model = new ChatModel(); |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Prefer dependency injection over direct instantiation for better testability.
The direct instantiation of ChatModel creates tight coupling and makes the controller difficult to unit test.
Consider using constructor injection:
-private final ChatModel model = new ChatModel();
+private final ChatModel model;
+
+public ChatController() {
+ this(new ChatModel());
+}
+
+// Package-private constructor for testing
+ChatController(ChatModel model) {
+ this.model = model;
+}This allows you to inject a mock ChatModel during testing.
🤖 Prompt for AI Agents
In src/main/java/com/example/ChatController.java around line 15, the controller
directly instantiates ChatModel which creates tight coupling and hinders unit
testing; change to constructor injection by removing the new ChatModel()
instantiation, add a constructor that accepts a ChatModel parameter and assigns
it to the final field, update any framework annotations if needed (e.g.,
@Autowired or leave as plain constructor for manual wiring), and update calling
code/tests to provide a mock or real ChatModel via the constructor.
| private void onSend() { | ||
| String message = inputField.getText().trim(); | ||
| if (!message.isEmpty()) { | ||
| messagesList.getItems().add("Me: " + message); | ||
| model.sendMessage(message); | ||
| inputField.clear(); | ||
| } | ||
| } |
There was a problem hiding this comment.
Add error handling for model.sendMessage().
The onSend() method should handle potential exceptions from model.sendMessage() to prevent the UI from becoming unresponsive.
Apply this diff to add error handling:
@FXML
private void onSend() {
String message = inputField.getText().trim();
if (!message.isEmpty()) {
- messagesList.getItems().add("Me: " + message);
- model.sendMessage(message);
- inputField.clear();
+ try {
+ messagesList.getItems().add("Me: " + message);
+ model.sendMessage(message);
+ inputField.clear();
+ } catch (Exception e) {
+ System.err.println("Failed to send message: " + e.getMessage());
+ // TODO: Show error dialog to user
+ }
}
}📝 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.
| private void onSend() { | |
| String message = inputField.getText().trim(); | |
| if (!message.isEmpty()) { | |
| messagesList.getItems().add("Me: " + message); | |
| model.sendMessage(message); | |
| inputField.clear(); | |
| } | |
| } | |
| private void onSend() { | |
| String message = inputField.getText().trim(); | |
| if (!message.isEmpty()) { | |
| try { | |
| messagesList.getItems().add("Me: " + message); | |
| model.sendMessage(message); | |
| inputField.clear(); | |
| } catch (Exception e) { | |
| System.err.println("Failed to send message: " + e.getMessage()); | |
| // TODO: Show error dialog to user | |
| } | |
| } | |
| } |
🤖 Prompt for AI Agents
In src/main/java/com/example/ChatController.java around lines 18-25, wrap the
call to model.sendMessage(...) with proper error handling and avoid blocking the
JavaFX thread: dispatch model.sendMessage(...) to a background thread (e.g.,
CompletableFuture.runAsync or a Task), catch any thrown exceptions, log them,
and then use Platform.runLater to update the UI (show an error Alert or mark the
message as failed) so the UI remains responsive; ensure inputField clearing and
messagesList updates occur on the JavaFX thread and adjust their order as needed
(e.g., show the sent message immediately, run sendMessage async, and on
exception notify the user).
| public void sendMessage(String message) { | ||
| // TODO: send POST JSON to ntfy | ||
| System.out.println("Sending message: " + message); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Add input validation to prevent null or empty messages.
The method should validate the input parameter to avoid processing invalid messages.
Apply this diff to add validation:
public void sendMessage(String message) {
+ if (message == null || message.isBlank()) {
+ throw new IllegalArgumentException("Message cannot be null or empty");
+ }
// TODO: send POST JSON to ntfy
System.out.println("Sending message: " + message);
}📝 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.
| public void sendMessage(String message) { | |
| // TODO: send POST JSON to ntfy | |
| System.out.println("Sending message: " + message); | |
| } | |
| public void sendMessage(String message) { | |
| if (message == null || message.isBlank()) { | |
| throw new IllegalArgumentException("Message cannot be null or empty"); | |
| } | |
| // TODO: send POST JSON to ntfy | |
| System.out.println("Sending message: " + message); | |
| } |
🤖 Prompt for AI Agents
In src/main/java/com/example/ChatModel.java around lines 5 to 8, the sendMessage
method currently does not validate its input; add a defensive check that the
message is not null and not blank (trim and verify length > 0) and if invalid
throw an IllegalArgumentException with a clear message (e.g., "message must not
be null or empty"); keep existing behavior for valid messages. Ensure the check
is the first statement in the method so invalid inputs are rejected before any
processing or I/O.
There was a problem hiding this comment.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
pom.xml(1 hunks)src/main/java/com/example/ChatController.java(1 hunks)src/main/java/com/example/ChatModel.java(1 hunks)src/main/java/com/example/HelloFX.java(1 hunks)src/main/java/module-info.java(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- pom.xml
- src/main/java/com/example/HelloFX.java
| 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(); |
There was a problem hiding this comment.
Fix the Content-Type/body mismatch to unblock sending.
We POST the raw message string but advertise Content-Type: application/json. The ntfy API rejects that combination with a 400/415 response, so nothing actually gets delivered even though the UI shows the message as sent. Align the header with the body (e.g., use text/plain; charset=UTF-8) or wrap the payload in proper JSON before posting.
Apply this diff to fix the header:
- .header("Content-Type", "application/json")
+ .header("Content-Type", "text/plain; charset=UTF-8")📝 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.
| 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(); | |
| HttpRequest request = HttpRequest.newBuilder() | |
| .uri(URI.create(sendUrl)) | |
| .header("Content-Type", "text/plain; charset=UTF-8") | |
| .header("X-Client-ID", clientId) | |
| .header("Title", "Friend: ") | |
| .POST(HttpRequest.BodyPublishers.ofString(message)) | |
| .build(); |
🤖 Prompt for AI Agents
In src/main/java/com/example/ChatModel.java around lines 44 to 50, the request
sets Content-Type: application/json while posting a raw message string; change
the Content-Type header to "text/plain; charset=UTF-8" (or alternatively wrap
the message in a proper JSON object) and ensure the request body is sent with
UTF-8 encoding so the ntfy API accepts the payload; update the header value and,
if applicable, use a UTF-8 string publisher or explicit encoding when building
the POST body.
| String raw = line.substring(5).trim(); | ||
| String msg = parseMessage(raw); | ||
|
|
||
| if (msg != null && !sentMessages.contains(msg)) { | ||
| Platform.runLater(() -> onMessageReceived.accept(msg)); | ||
| } | ||
| } | ||
| } | ||
| } catch (Exception e) { | ||
| e.printStackTrace(); | ||
| } | ||
| }) | ||
| .exceptionally(e -> { e.printStackTrace(); return null; }); | ||
| } | ||
|
|
||
| private 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("\\\"", "\""); |
There was a problem hiding this comment.
Don't drop legitimate messages that match our recent text.
sentMessages.contains(msg) only compares the text, so any remote user who sends the same text within the five-second eviction window is silently filtered out. Because we already stamp each send with X-Client-ID, the SSE payload contains "client" for every event—compare that to clientId instead. That lets us remove the whole sentMessages bookkeeping and still suppress only our own echoes.
Apply this diff to switch to client-based filtering:
- String raw = line.substring(5).trim();
- String msg = parseMessage(raw);
-
- if (msg != null && !sentMessages.contains(msg)) {
- Platform.runLater(() -> onMessageReceived.accept(msg));
- }
+ String raw = line.substring(5).trim();
+ MessagePayload payload = parseMessage(raw);
+
+ if (payload != null && !clientId.equals(payload.client())) {
+ Platform.runLater(() -> onMessageReceived.accept(payload.message()));
+ }And adjust parseMessage to surface the client id:
- private String parseMessage(String data) {
+ private MessagePayload 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("\\\"", "\"");
+ String message = msgMatcher.group(1).replace("\\\"", "\"");
+ Matcher clientMatcher = Pattern.compile("\"client\"\\s*:\\s*\"(.*?)\"").matcher(data);
+ String client = clientMatcher.find() ? clientMatcher.group(1) : null;
+ return new MessagePayload(message, client);
}
} catch (Exception ignored) {}
return null;
}
+
+ private record MessagePayload(String message, String client) {}Once this is in place you can drop the sentMessages set and its cleanup thread entirely—they are no longer needed.
Summary by CodeRabbit
New Features
Chores