-
Notifications
You must be signed in to change notification settings - Fork 66
Feature #30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature #30
Changes from all commits
8fa0416
f4b6e96
3f53fac
6ea1311
ac2dccc
e59bb6e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,3 @@ | ||
| target/ | ||
| /.idea/ | ||
| .env |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <module version="4"> | ||
| <component name="AdditionalModuleElements"> | ||
| <content url="file://$MODULE_DIR$" dumb="true"> | ||
| <sourceFolder url="file://$MODULE_DIR$/src/test" isTestSource="true" /> | ||
| </content> | ||
| </component> | ||
| </module> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| package com.example; | ||
|
|
||
| public class ChatMessage { | ||
| private final String id; | ||
| private final String username; | ||
| private final String message; | ||
| private final String timestamp; | ||
| private final String fileName; | ||
| private final String fileUrl; | ||
| private final String mimeType; | ||
|
|
||
| public ChatMessage(String id, String username, String message, String timestamp, | ||
| String fileName, String fileUrl, String mimeType) { | ||
| this.id = id; | ||
| this.username = username; | ||
| this.message = message; | ||
| this.timestamp = timestamp; | ||
| this.fileName = fileName; | ||
| this.fileUrl = fileUrl; | ||
| this.mimeType = mimeType; | ||
| } | ||
|
|
||
| public ChatMessage(String id, String username, String message, String timestamp) { | ||
| this(id, username, message, timestamp, null, null, null); | ||
| } | ||
|
|
||
| public String getId() { return id; } | ||
| public String getUsername() { return username; } | ||
| public String getMessage() { return message; } | ||
| public String getTimestamp() { return timestamp; } | ||
| public String getFileName() { return fileName; } | ||
| public String getFileUrl() { return fileUrl; } | ||
| public String getMimeType() { return mimeType; } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| package com.example; | ||
|
|
||
| import java.io.BufferedReader; | ||
| import java.io.FileReader; | ||
| import java.io.IOException; | ||
| import java.util.HashMap; | ||
| import java.util.Map; | ||
|
|
||
| public class EnvLoader { | ||
|
|
||
| private static Map<String, String> env = new HashMap<>(); | ||
|
|
||
| static { | ||
| try (BufferedReader reader = new BufferedReader(new FileReader(".env"))) { | ||
| String line; | ||
| while ((line = reader.readLine()) != null) { | ||
| line = line.trim(); | ||
| if (line.isEmpty() || line.startsWith("#")) continue; | ||
| String[] parts = line.split("=", 2); | ||
| if (parts.length == 2) { | ||
| env.put(parts[0].trim(), parts[1].trim()); | ||
| } | ||
| } | ||
| } catch (IOException e) { | ||
| System.err.println("Warning: .env file not found or could not be read"); | ||
| } | ||
| } | ||
|
|
||
| public static String get(String key) { | ||
| return env.get(key); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,22 +1,120 @@ | ||
| package com.example; | ||
|
|
||
| import javafx.application.Platform; | ||
| import javafx.collections.FXCollections; | ||
| import javafx.collections.ObservableList; | ||
| import javafx.collections.transformation.FilteredList; | ||
| import javafx.fxml.FXML; | ||
| import javafx.scene.control.Label; | ||
| import javafx.fxml.Initializable; | ||
| import javafx.scene.control.CheckBox; | ||
| import javafx.scene.control.Hyperlink; | ||
| import javafx.scene.control.ListCell; | ||
| import javafx.scene.control.ListView; | ||
| import javafx.scene.control.TextField; | ||
| import javafx.scene.image.Image; | ||
| import javafx.scene.image.ImageView; | ||
| import javafx.scene.text.Text; | ||
| import javafx.scene.text.TextFlow; | ||
| import javafx.stage.FileChooser; | ||
|
|
||
| /** | ||
| * Controller layer: mediates between the view (FXML) and the model. | ||
| */ | ||
| public class HelloController { | ||
| import java.io.File; | ||
| import java.net.URL; | ||
| import java.time.format.DateTimeFormatter; | ||
| import java.util.ResourceBundle; | ||
| import java.util.function.Predicate; | ||
|
|
||
| public class HelloController implements Initializable { | ||
|
|
||
| @FXML private ListView<ChatMessage> chatList; | ||
| @FXML private TextField inputField; | ||
| @FXML private TextField usernameField; | ||
| @FXML private CheckBox hideMyMessagesCheck; | ||
|
|
||
| private final HelloModel model = new HelloModel(); | ||
| private final ObservableList<ChatMessage> masterList = FXCollections.observableArrayList(); | ||
| private FilteredList<ChatMessage> filteredList; | ||
|
|
||
| private String getCurrentUsername() { | ||
| String u = usernameField.getText(); | ||
| if (u == null) return "Anonymous"; | ||
| u = u.trim(); | ||
| return u.isEmpty() ? "Anonymous" : u; | ||
| } | ||
|
|
||
| @Override | ||
| public void initialize(URL url, ResourceBundle rb) { | ||
| filteredList = new FilteredList<>(masterList, msg -> true); | ||
| chatList.setItems(filteredList); | ||
|
|
||
| chatList.setCellFactory(list -> new ListCell<>() { | ||
| @Override | ||
| protected void updateItem(ChatMessage msg, boolean empty) { | ||
| super.updateItem(msg, empty); | ||
| if (empty || msg == null) { setGraphic(null); return; } | ||
|
|
||
| Text user = new Text(msg.getUsername()); | ||
| user.setStyle("-fx-font-weight: bold;"); | ||
| Text time = new Text(" (" + msg.getTimestamp() + ")\n"); | ||
| time.setStyle("-fx-fill: gray; -fx-font-size: 12px;"); | ||
|
|
||
| if (msg.getFileName() != null && msg.getFileUrl() != null) { | ||
| if (msg.getMimeType() != null && msg.getMimeType().startsWith("image/")) { | ||
| try { | ||
| ImageView imageView = new ImageView(new Image(msg.getFileUrl(), true)); | ||
| imageView.setFitWidth(200); | ||
| imageView.setPreserveRatio(true); | ||
| Text messageText = new Text(msg.getMessage() + "\n"); | ||
| messageText.setStyle("-fx-font-size: 14px;"); | ||
| setGraphic(new TextFlow(user, time, messageText, imageView)); | ||
| } catch (Exception e) { e.printStackTrace(); } | ||
| } else { | ||
| Hyperlink link = new Hyperlink(msg.getFileName()); | ||
| final String fileUrl = msg.getFileUrl(); | ||
| link.setOnAction(ev -> HelloFX.hostServices().showDocument(fileUrl)); | ||
| Text messageText = new Text(msg.getMessage() + "\n"); | ||
| messageText.setStyle("-fx-font-size: 14px;"); | ||
| setGraphic(new TextFlow(user, time, messageText, link)); | ||
| } | ||
| } else { | ||
| Text text = new Text(msg.getMessage()); | ||
| text.setStyle("-fx-font-size: 14px;"); | ||
| setGraphic(new TextFlow(user, time, text)); | ||
| } | ||
| } | ||
| }); | ||
|
|
||
| hideMyMessagesCheck.selectedProperty().addListener((obs, oldVal, newVal) -> updateFilterPredicate()); | ||
| usernameField.textProperty().addListener((obs, oldVal, newVal) -> updateFilterPredicate()); | ||
|
|
||
| model.loadHistory(msg -> Platform.runLater(() -> masterList.add(msg))); | ||
| model.listenForMessages(msg -> Platform.runLater(() -> masterList.add(msg))); | ||
| } | ||
|
|
||
| private void updateFilterPredicate() { | ||
| final String current = getCurrentUsername(); | ||
| final boolean hideMine = hideMyMessagesCheck.isSelected(); | ||
| Predicate<ChatMessage> pred = msg -> !hideMine || !current.equals(msg.getUsername()); | ||
| filteredList.setPredicate(pred); | ||
| } | ||
|
|
||
| @FXML | ||
| private Label messageLabel; | ||
| private void onSend() { | ||
| String user = getCurrentUsername(); | ||
| String msg = inputField.getText().trim(); | ||
| if (msg.isEmpty()) return; | ||
| inputField.clear(); | ||
|
|
||
| new Thread(() -> { | ||
| try { model.sendMessage(user, msg); } | ||
| catch (Exception e) { e.printStackTrace(); } | ||
| }).start(); | ||
| } | ||
|
|
||
| @FXML | ||
| private void initialize() { | ||
| if (messageLabel != null) { | ||
| messageLabel.setText(model.getGreeting()); | ||
| } | ||
| private void onAttachFile() { | ||
| FileChooser fileChooser = new FileChooser(); | ||
| fileChooser.setTitle("Select a file to send"); | ||
| File file = fileChooser.showOpenDialog(chatList.getScene().getWindow()); | ||
| if (file != null) new Thread(() -> model.sendFile(getCurrentUsername(), file)).start(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,25 +1,32 @@ | ||||||
| package com.example; | ||||||
|
|
||||||
| import javafx.application.Application; | ||||||
| import javafx.application.HostServices; | ||||||
| import javafx.fxml.FXMLLoader; | ||||||
| import javafx.scene.Parent; | ||||||
| import javafx.scene.Scene; | ||||||
| import javafx.stage.Stage; | ||||||
|
|
||||||
| public class HelloFX extends Application { | ||||||
| public static HostServices hostServices; | ||||||
|
|
||||||
| public static HostServices hostServices() { | ||||||
| return hostServices; | ||||||
| } | ||||||
|
|
||||||
| @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); | ||||||
| stage.setTitle("Hello MVC"); | ||||||
| hostServices = getHostServices(); | ||||||
| FXMLLoader loader = new FXMLLoader(getClass().getResource("/com/example/hello-view.fxml")); | ||||||
| Parent root = loader.load(); | ||||||
| Scene scene = new Scene(root, 720, 520); | ||||||
| scene.getStylesheets().add(getClass().getResource("/css/style.css").toExternalForm()); | ||||||
| stage.setTitle("JavaFX NTFY Chat — mats_notiser"); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove hardcoded username from window title. The window title includes a hardcoded username "mats_notiser", which appears to be developer-specific. Since the UI includes a
Example for a generic title: - stage.setTitle("JavaFX NTFY Chat — mats_notiser");
+ stage.setTitle("JavaFX NTFY Chat");📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| stage.setScene(scene); | ||||||
| stage.show(); | ||||||
| } | ||||||
|
|
||||||
| public static void main(String[] args) { | ||||||
| launch(); | ||||||
| launch(args); | ||||||
| } | ||||||
|
|
||||||
| } | ||||||
| } | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Verify dependency versions and security advisories.
Since the project uses fixed versions for
org.json(20240303, from March 2024) andhttpclient5(5.3.1), please verify these are current and check for any security advisories.Run the following script to check for the latest versions and security advisories:
🏁 Script executed:
Length of output: 1094
Update outdated dependencies in pom.xml.
HttpClient 5.5.x is stable and production ready, and actively maintained, while the current version (5.3.1) is from an older release series. Latest stable httpclient5 is 5.5.1.
org.json latest version is 20250517 (May 2025), but the pom.xml uses 20240303 (March 2024)—17 months behind. Both current versions lack recent security patches and improvements.
Update:
org.json:jsonfrom20240303to20250517httpclient5from5.3.1to5.5.1🤖 Prompt for AI Agents