Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
b83bd2c
Add chat functionality, UI enhancements, and first update controller …
annikaholmqvist94 Oct 29, 2025
67cc5f2
Update .gitignore
annikaholmqvist94 Nov 3, 2025
1b9b0b8
Implement message sending/receiving logic with Ntfy integration and u…
annikaholmqvist94 Nov 4, 2025
d3a2149
Add file attachment button and stub method in controller
annikaholmqvist94 Nov 4, 2025
6584d5b
Implement file upload functionality with Ntfy integration
annikaholmqvist94 Nov 6, 2025
17546f9
Add unit tests for message receiving and file sending
annikaholmqvist94 Nov 6, 2025
e80bd27
Refactor `HelloModel` for JavaFX thread update tests for enhanced mes…
annikaholmqvist94 Nov 7, 2025
a69df76
Add validation for empty or whitespace messages in `HelloModel.sendMe…
annikaholmqvist94 Nov 7, 2025
0877a20
Add topic-based messaging and file sharing with dynamic UI updates an…
annikaholmqvist94 Nov 12, 2025
37fe2ce
Add support for dynamic topic selection update tests and model logic…
annikaholmqvist94 Nov 12, 2025
32adbde
Add fixed-topic chat functionality, update file sending message/file …
annikaholmqvist94 Nov 13, 2025
fe3d0d0
update tests for enhanced verification and UI consistency.
annikaholmqvist94 Nov 13, 2025
fc0902a
update tests for enhanced verification and UI consistency.
annikaholmqvist94 Nov 13, 2025
0afcc08
update tests for enhanced verification and UI consistency.
annikaholmqvist94 Nov 13, 2025
065761a
Add Javadoc comments for improved code clarity and maintainability.
annikaholmqvist94 Nov 14, 2025
e04e60b
Refactor `HelloModel` to enhance async message/file sending logic and…
annikaholmqvist94 Nov 14, 2025
10acd5d
Add `HOST_NAME` validation in `HelloController` and improve error han…
annikaholmqvist94 Nov 14, 2025
5b0a5bf
Improve file attachment handling in `HelloController`, enhance error …
annikaholmqvist94 Nov 14, 2025
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
Empty file modified mvnw
100644 → 100755
Empty file.
19 changes: 19 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@
<artifactId>javafx-controls</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.19.0</version>
</dependency>
<dependency>
<groupId>io.github.cdimascio</groupId>
<artifactId>dotenv-java</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
Expand All @@ -63,6 +73,15 @@
<noManPages>true</noManPages>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>25</source>
<target>25</target>
<compilerArgs>--enable-preview</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
</project>
205 changes: 198 additions & 7 deletions src/main/java/com/example/HelloController.java
Original file line number Diff line number Diff line change
@@ -1,22 +1,213 @@
package com.example;

import javafx.beans.binding.Bindings;

import javafx.fxml.FXML;
import javafx.scene.control.Label;

import javafx.scene.control.*;

import javafx.stage.FileChooser;
import javafx.stage.Stage;

import java.io.File;



/**
* Controller layer: mediates between the view (FXML) and the model.
* Controller layer: Manages the user interface and mediates communication between the View (FXML) and the Model (HelloModel).
* Handles user interactions such as sending messages, attaching files, and displaying chat content.
*/
public class HelloController {

private final HelloModel model = new HelloModel();

private static final String HOST_NAME = System.getenv("HOST_NAME");

static {
if (HOST_NAME == null || HOST_NAME.isBlank()) {
throw new IllegalStateException(
"Environment variable HOST_NAME must be set to the server URL."
);
}
}





/**
* The model instance that holds application data and business logic.
* Initializes connection to the specified Ntfy server.
*/
private final HelloModel model = new HelloModel(new NtfyConnectionImpl(HOST_NAME));


/**
* The ListView component displaying the list of chat messages.
*/
@FXML
private Label messageLabel;
public ListView<NtfyMessageDto> chatListView;

/**
* The label displaying the current fixed topic being used.
*/
@FXML
public Label topicLabel;

/**
* The label indicating the name of the file currently attached for sending.
*/
@FXML
public Label attachedFileLabel;


/**
* The button used to send the message or the attached file.
*/
@FXML
private Button sendButton;

@FXML
private TextField messageInput;

@FXML
private Button attachFile;


/**
* Initializes the controller. This method is called automatically by the FXML loader.
* It sets up bindings between the view components and the model and configures the chat list view.
*/
@FXML
private void initialize() {
if (messageLabel != null) {
messageLabel.setText(model.getGreeting());
if (sendButton != null) {
sendButton.setText(model.getGreeting());

// Bindning: Knappen är inaktiverad ENDAST om BÅDE meddelandet är tomt OCH fil inte är bifogad
sendButton.disableProperty().bind(
Bindings.createBooleanBinding(() -> {
boolean isMessageEmpty = model.messageToSendProperty().get() == null ||
model.messageToSendProperty().get().trim().isEmpty();
boolean isFileNotAttached = model.fileToSendProperty().get() == null;

return isMessageEmpty && isFileNotAttached;
},
model.messageToSendProperty(),
model.fileToSendProperty())
);
}

if (topicLabel != null) {
// Visar den fasta topicen
topicLabel.textProperty().bind(
Bindings.concat("Fixed Topic: ", model.currentTopicProperty())
);
}

// Hanterar visning av bifogad fil
if (attachedFileLabel != null) {
model.fileToSendProperty().addListener((obs, oldFile, newFile) -> {
if (newFile != null) {
attachedFileLabel.setText("Attached file: " + newFile.getName());
attachedFileLabel.setStyle("-fx-font-style: italic;" +
" -fx-font-size: 12px;" +
" -fx-text-fill: #008000;");
} else {
attachedFileLabel.setText("No file attached");
attachedFileLabel.setStyle("-fx-font-style: italic; " +
"-fx-font-size: 12px; " +
"-fx-text-fill: #333;");
}
});
attachedFileLabel.setText("No file attached");
}

if (messageInput!=null){
messageInput.textProperty().bindBidirectional(model.messageToSendProperty());
}

if(chatListView!=null){
chatListView.setItems(model.getMessages());
// Använd den enkla CellFactoryn
chatListView.setCellFactory(param -> new SimpleMessageCell());
}
}

/**
* A simple custom ListCell implementation for the chatListView.
* It displays the message text or a placeholder for file uploads, and indicates if the message was sent locally.
*/
private static class SimpleMessageCell extends ListCell<NtfyMessageDto> {

@Override
protected void updateItem(NtfyMessageDto item, boolean empty) {
super.updateItem(item, empty);

if (empty || item == null) {
setText(null);
setGraphic(null);
setStyle(null);
} else {
// Hämta meddelandet eller visa filstatus om meddelandet är tomt
String displayMessage = item.message() != null && !item.message().trim().isEmpty()
? item.message()
: ("file".equals(item.event())) ? item.topic() + " Uploaded" : "";

// Lägg till prefix för att visa om det är skickat lokalt
String prefix = item.isLocal() ? "(Sent) " : "";

setText(prefix + displayMessage);
setGraphic(null);

// Mycket enkel stil utan bubblor/färger. Använd standard utseende.
setStyle(null);
}
}
}


/**
* Handles the action of the send button.
* If a file is attached, it calls the model to send the file; otherwise, it calls the model to send the text message.
*/
@FXML
protected void sendMessage() {
if (model.fileToSendProperty().get() != null) {
// Om en fil är bifogad, skicka filen och rensa bilagan i modellen
model.sendFile();
} else {
// Annars, skicka textmeddelandet
model.sendMessage();
}

if (messageInput!=null){
messageInput.requestFocus();
}
}

/**
* Handles the action of the attach file button.
* Opens a FileChooser dialog and sets the selected file in the model.
*/
@FXML
protected void attachFile() {
// Hämta scenen från en av kontrollerna

if (attachFile == null || attachFile.getScene() == null || attachFile.getScene().getWindow() == null) {
System.err.println("Cannot open file chooser: UI not ready.");
return;
}
Stage stage = (Stage) attachFile.getScene().getWindow();




FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Choose file to attach");

File selectedFile = fileChooser.showOpenDialog(stage);

if (selectedFile != null) {
model.setFileToSend(selectedFile);
}
}
}
}
4 changes: 4 additions & 0 deletions src/main/java/com/example/HelloFX.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.example;


import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
Expand All @@ -10,6 +11,9 @@ 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
Loading
Loading