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
74 changes: 74 additions & 0 deletions HelloController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.example;

import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;

import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

public class HelloController {


@FXML
private Button logoutButton;
@FXML
private AnchorPane scenePane;
@FXML
private ListView<String> chatList;
@FXML
private TextField messageField;
@FXML
private TextField usernameField;

private NtfyClient ntfy;
private final DateTimeFormatter timeFormat = DateTimeFormatter.ofPattern("HH:mm");


@FXML
public void initialize() {
// Set your topic name (you can make this configurable)
String topic = "myfxchat";
ntfy = new NtfyClient(topic);

// Start listening to the topic
ntfy.subscribe(message -> Platform.runLater(() -> chatList.getItems().add(message)));
}

@FXML
private void handleSend() {
String username = usernameField.getText().isBlank() ? "Anonymous" : usernameField.getText();
String message = messageField.getText().trim();
if (message.isEmpty()) return;

String time = LocalTime.now().format(timeFormat);
String formatted = String.format("[%s] %s: %s", time, username, message);
chatList.getItems().add(formatted);

ntfy.sendMessage(username, message);
messageField.clear();
}

Stage stage;
public void logout(ActionEvent event) {
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle("Log out");
alert.setHeaderText("Log out");
alert.setContentText("Are you sure you want to logout?");

if(alert.showAndWait().get() == ButtonType.OK){
stage = (Stage) scenePane.getScene().getWindow();
System.out.println("You have been logged out");
stage.close();
}
}
}






10 changes: 10 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.github.cdimascio</groupId>
<artifactId>dotenv-java</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20240303</version>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
Expand Down
91 changes: 82 additions & 9 deletions src/main/java/com/example/HelloController.java
Original file line number Diff line number Diff line change
@@ -1,22 +1,95 @@
package com.example;

import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.*;
import javafx.scene.layout.AnchorPane;
import javafx.stage.FileChooser;
import javafx.stage.Stage;

import java.io.File;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

/**
* Controller layer: mediates between the view (FXML) and the model.
*/
public class HelloController {

private final HelloModel model = new HelloModel();

@FXML
private Label messageLabel;
private Button logoutButton;
@FXML
private AnchorPane scenePane;
@FXML
private ListView<String> chatList;
@FXML
private TextField messageField;
@FXML
private TextField usernameField;



private NtfyClient ntfy;
private final DateTimeFormatter timeFormat = DateTimeFormatter.ofPattern("HH:mm");



@FXML
public void initialize() {
// Set your topic name (you can make this configurable)
String topic = "myfxchat";
ntfy = new NtfyClient(topic);

// Start listening to the topic
ntfy.subscribe(message -> Platform.runLater(() -> chatList.getItems().add(message)));
}

@FXML
private void initialize() {
if (messageLabel != null) {
messageLabel.setText(model.getGreeting());
private void handleSend() {
String username = usernameField.getText().isBlank() ? "Anonymous" : usernameField.getText();
String message = messageField.getText().trim();
if (message.isEmpty()) return;

String time = LocalTime.now().format(timeFormat);
String formatted = String.format("[%s] %s: %s", time, username, message);
chatList.getItems().add(formatted);

ntfy.sendMessage(username, message);
messageField.clear();
}
@FXML
private void handleAttachFile(ActionEvent event) {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Select a file to send");

File selectedFile = fileChooser.showOpenDialog(null);

if (selectedFile != null) {
System.out.println("Selected file: " + selectedFile.getAbsolutePath());
sendFileToBackend(selectedFile);
}
}

private void sendFileToBackend(File selectedFile) {
}
Comment on lines +72 to +73
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

Don’t leave file sending as a no-op

handleAttachFile promises to send the selected file, but sendFileToBackend is empty, so every attachment is silently dropped. Either implement the upload/publish flow (or at least queue the event into NtfyClient) or remove/disable the UI action until the backend exists so users aren’t misled.

🤖 Prompt for AI Agents
In src/main/java/com/example/HelloController.java around lines 72-73,
sendFileToBackend is a no-op causing attachments to be dropped; implement the
method to either perform the upload or queue the file with NtfyClient.
Specifically: validate the selectedFile exists and is readable, then either (A)
perform an HTTP multipart upload to the configured backend endpoint with proper
content-type and error handling, updating the UI on success/failure, or (B) if
using NtfyClient, call its publish/queue method with the file stream/bytes and
metadata, handle exceptions and update the UI accordingly; if no backend is
configured, disable the attach action or show a clear user-facing message so
attachments are not silently lost.


Stage stage;
public void logout(ActionEvent event) {
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle("Log out");
alert.setHeaderText("Log out");
alert.setContentText("Are you sure you want to logout?");

if(alert.showAndWait().get() == ButtonType.OK){
stage = (Stage) scenePane.getScene().getWindow();
System.out.println("You have been logged out");
stage.close();
Comment on lines +82 to +85
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

Handle showAndWait()’s Optional safely

Alert.showAndWait() returns an Optional<ButtonType>; calling .get() without checking emptiness will throw if the user closes the dialog via ESC or the window’s close button. Guard the Optional (e.g., by using if (alert.showAndWait().filter(ButtonType.OK::equals).isPresent())) before acting on the selection.

Apply this diff:

-        if(alert.showAndWait().get() == ButtonType.OK){
-        stage = (Stage) scenePane.getScene().getWindow();
-        System.out.println("You have been logged out");
-        stage.close();
-        }
+        alert.showAndWait()
+             .filter(ButtonType.OK::equals)
+             .ifPresent(btn -> {
+                 stage = (Stage) scenePane.getScene().getWindow();
+                 System.out.println("You have been logged out");
+                 stage.close();
+             });
📝 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
if(alert.showAndWait().get() == ButtonType.OK){
stage = (Stage) scenePane.getScene().getWindow();
System.out.println("You have been logged out");
stage.close();
alert.showAndWait()
.filter(ButtonType.OK::equals)
.ifPresent(btn -> {
stage = (Stage) scenePane.getScene().getWindow();
System.out.println("You have been logged out");
stage.close();
});
🤖 Prompt for AI Agents
In src/main/java/com/example/HelloController.java around lines 82 to 85, the
code calls alert.showAndWait().get() directly which can throw if the Optional is
empty; replace this direct .get() usage with a safe Optional check such as using
alert.showAndWait().filter(ButtonType.OK::equals).isPresent() (or
alert.showAndWait().ifPresent(bt -> { if (bt == ButtonType.OK) { ... } })) and
only perform the stage lookup, println and stage.close() inside that guarded
branch.

}

}
}






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

import javafx.fxml.FXML;

import javafx.scene.control.Label;


public class HelloController2 {

@FXML
private Label nameLabel;

public void displayName(String userName) {
nameLabel.setText("Hello: " + userName);
}
}
78 changes: 77 additions & 1 deletion src/main/java/com/example/HelloFX.java
Original file line number Diff line number Diff line change
@@ -1,23 +1,99 @@
package com.example;


import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.stage.FileChooser;
import javafx.stage.Stage;

import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Objects;

public class HelloFX extends Application {

@Override
public void start(Stage stage) throws Exception {
FXMLLoader fxmlLoader = new FXMLLoader(HelloFX.class.getResource("hello-view.fxml"));
// Load the FXML file (absolute path is safer)
FXMLLoader fxmlLoader = new FXMLLoader(HelloFX.class.getResource("/com/example/hello-view.fxml"));
//FXMLLoader loader = new FXMLLoader(getClass().getResource("/com/example/hello-view2.fxml"));
stage.setTitle("JavaFX Chat (ntfy)");
Comment on lines +27 to +29
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

Remove commented code and fix duplicate title setting.

Line 28 contains commented-out code that should be removed. Additionally, the title is set on line 29 but then set again on line 44 with a different value. Decide on one title and remove the duplicate.

-        // Load the FXML file (absolute path is safer)
         FXMLLoader fxmlLoader = new FXMLLoader(HelloFX.class.getResource("/com/example/hello-view.fxml"));
-        //FXMLLoader loader = new FXMLLoader(getClass().getResource("/com/example/hello-view2.fxml"));
-        stage.setTitle("JavaFX Chat (ntfy)");

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/main/java/com/example/HelloFX.java around lines 27 to 29, remove the
commented-out FXMLLoader line at 28 and consolidate the window title so it is
set only once: choose the desired title (either "JavaFX Chat (ntfy)" or the
value used later at line 44), keep a single stage.setTitle(...) call with that
chosen string, and delete the other duplicate setTitle call (the one at line 44
or the one here) so the title is not applied twice.

Parent root = fxmlLoader.load();

// Create the scene
Scene scene = new Scene(root, 640, 480);

// Add the stylesheet (optional)
scene.getStylesheets().add(
Objects.requireNonNull(
getClass().getResource("/application.css"),
"Missing resource: application.css"
).toExternalForm()
);

// Show the window
stage.setTitle("Hello MVC");
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

Duplicate title setting - choose one.

The stage title is already set on line 29 as "JavaFX Chat (ntfy)". This line sets it again to "Hello MVC". Remove one of these duplicate calls.

-        stage.setTitle("Hello MVC");
📝 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
stage.setTitle("Hello MVC");
🤖 Prompt for AI Agents
In src/main/java/com/example/HelloFX.java around line 44, there is a duplicate
stage.setTitle call that resets the window title to "Hello MVC" after it was
already set to "JavaFX Chat (ntfy)" on line 29; remove the redundant
stage.setTitle("Hello MVC") (or alternatively remove the earlier one) so the
title is only set once and keep the desired string "JavaFX Chat (ntfy)".


stage.setScene(scene);
stage.show();

stage.setOnCloseRequest(event -> {
event.consume();
logout(stage);

});
}

private void sendFileToBackend(File file) {
try {
String backendUrl = System.getenv("BACKEND_URL");
if (backendUrl == null) {
System.err.println("BACKEND_URL not set");
return;
}

HttpURLConnection conn = (HttpURLConnection) new URL(backendUrl).openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/octet-stream");
conn.setRequestProperty("Title", file.getName());

try (OutputStream os = conn.getOutputStream();
FileInputStream fis = new FileInputStream(file)) {
fis.transferTo(os);
}

int responseCode = conn.getResponseCode();
System.out.println("Upload response: " + responseCode);
conn.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
public void logout(Stage stage) {

Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle("Logout");
alert.setHeaderText("You are about to log out");
alert.setContentText("Do you want to save before exiting?");

if(alert.showAndWait().get() == ButtonType.OK){
//stage = (Stage) scenePane.getScene().getWindow();
System.out.println("You successfully logged out");
stage.close();
}
}


public static void main(String[] args) {
launch();
}
Expand Down
23 changes: 16 additions & 7 deletions src/main/java/com/example/HelloModel.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
package com.example;

import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
Comment on lines +3 to +4
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

Drop unused JavaFX property imports

SimpleStringProperty and StringProperty are no longer referenced in this class, and javac will fail with “import ... is never used”. Please remove the stale imports to restore compilation.

Apply this diff:

-import javafx.beans.property.SimpleStringProperty;
-import javafx.beans.property.StringProperty;
📝 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
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
🤖 Prompt for AI Agents
In src/main/java/com/example/HelloModel.java around lines 3 to 4, the imports
for javafx.beans.property.SimpleStringProperty and
javafx.beans.property.StringProperty are unused and cause compilation errors;
remove these two import lines from the top of the file so only actually
referenced imports remain.


import java.util.List;
import java.util.ArrayList;

/**
* Model layer: encapsulates application data and business logic.
*/
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 List<String> messages = new ArrayList<>();

public void addMessage(String message) {
messages.add(message);
}

public List<String> getMessages() {
return messages;

}
}
Loading
Loading