-
Notifications
You must be signed in to change notification settings - Fork 66
Chat client nyast #46
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
base: main
Are you sure you want to change the base?
Changes from all commits
729c4ff
895f0b5
e3ebf9e
3133193
0916046
9284a5d
27ea848
0c3274f
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 |
|---|---|---|
| @@ -1,22 +1,70 @@ | ||
| package com.example; | ||
|
|
||
| import javafx.collections.ListChangeListener; | ||
| import javafx.fxml.FXML; | ||
| import javafx.scene.control.Label; | ||
| import javafx.scene.control.Button; | ||
| import javafx.scene.control.TextArea; | ||
| import javafx.scene.input.KeyCode; | ||
| import javafx.stage.FileChooser; | ||
|
|
||
| import java.io.File; | ||
|
|
||
| /** | ||
| * Controller layer: mediates between the view (FXML) and the model. | ||
| */ | ||
| public class HelloController { | ||
|
|
||
| private final HelloModel model = new HelloModel(); | ||
| @FXML private TextArea inputField; | ||
| @FXML private Button sendButton; | ||
| @FXML private TextArea chatArea; | ||
| @FXML private Button sendFileButton; | ||
|
|
||
|
|
||
| public final HelloModel model = new HelloModel(new NtfyConnectionImpl()); | ||
|
|
||
| @FXML | ||
| private Label messageLabel; | ||
|
|
||
| @FXML | ||
| private void initialize() { | ||
| if (messageLabel != null) { | ||
| messageLabel.setText(model.getGreeting()); | ||
|
|
||
| model.getMessages().addListener((ListChangeListener<NtfyMessageDto>) change ->{ | ||
| while(change.next()) { | ||
| if (change.wasAdded()) { | ||
| for (var msg : change.getAddedSubList()) { | ||
| chatArea.appendText(msg.message() + "\n"); | ||
| } | ||
| } | ||
| } | ||
| }); | ||
|
|
||
| sendFileButton.setOnAction(e -> attachFile()); | ||
| sendButton.setOnAction(e -> sendMessage()); | ||
|
|
||
| inputField.setOnKeyPressed(event -> { | ||
| if (event.getCode() == KeyCode.ENTER && !event.isShiftDown()) { | ||
| sendMessage(); | ||
| event.consume(); | ||
| } | ||
| }); | ||
| } | ||
| @FXML | ||
| private void sendMessage() { | ||
| String msg = inputField.getText().trim(); | ||
| if (msg.isEmpty()) return; | ||
|
|
||
| chatArea.appendText("Du: " + msg + "\n"); | ||
| model.setMessageToSend(msg); | ||
| model.sendMessage(); | ||
| inputField.clear(); | ||
| } | ||
| @FXML | ||
| private void attachFile() { | ||
| FileChooser chooser = new FileChooser(); | ||
| chooser.setTitle("Välj fil att bifoga"); | ||
|
|
||
| File file = chooser.showOpenDialog(sendFileButton.getScene().getWindow()); | ||
| if (file != null) { | ||
| model.sendFile(file); | ||
| chatArea.appendText("Du skickade fil: " + file.getName() + "\n"); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,15 +1,78 @@ | ||||||||||||||||||||||||||||||||
| package com.example; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| 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.*; | ||||||||||||||||||||||||||||||||
| import java.net.URI; | ||||||||||||||||||||||||||||||||
| import java.net.http.HttpRequest; | ||||||||||||||||||||||||||||||||
| import java.net.http.HttpResponse; | ||||||||||||||||||||||||||||||||
| import java.util.UUID; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||
| * 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 NtfyConnection connection; | ||||||||||||||||||||||||||||||||
| private final StringProperty messageToSend = new SimpleStringProperty(""); | ||||||||||||||||||||||||||||||||
| private final ObservableList<NtfyMessageDto> messages = FXCollections.observableArrayList(); | ||||||||||||||||||||||||||||||||
| private final String clientId = UUID.randomUUID().toString(); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| //Konstuktor för prod | ||||||||||||||||||||||||||||||||
| public HelloModel() { | ||||||||||||||||||||||||||||||||
| this.connection = new NtfyConnectionImpl(); | ||||||||||||||||||||||||||||||||
| receiveMessage(); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| //Konstuktor för test | ||||||||||||||||||||||||||||||||
| public HelloModel(NtfyConnection connection) { | ||||||||||||||||||||||||||||||||
| this.connection = connection; | ||||||||||||||||||||||||||||||||
| receiveMessage(); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| public ObservableList<NtfyMessageDto> getMessages() { | ||||||||||||||||||||||||||||||||
| return messages; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| public void setMessageToSend(String message) { | ||||||||||||||||||||||||||||||||
| messageToSend.set(message); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| public void sendMessage() { | ||||||||||||||||||||||||||||||||
| String text = messageToSend.get().trim(); | ||||||||||||||||||||||||||||||||
| if (text.isEmpty()) return; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| connection.send(text); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| messageToSend.set(""); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| public void sendFile(File file) { | ||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||
| connection.sendFile(file.toPath()); | ||||||||||||||||||||||||||||||||
| } catch (IOException e) { | ||||||||||||||||||||||||||||||||
| e.printStackTrace(); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
Comment on lines
+56
to
+62
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.
The method catches - public void sendFile(File file) {
+ public boolean sendFile(File file) {
try {
- connection.sendFile(file.toPath());
+ return connection.sendFile(file.toPath());
} catch (IOException e) {
e.printStackTrace();
+ return false;
}
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| public void receiveMessage() { | ||||||||||||||||||||||||||||||||
| connection.receive(m -> | ||||||||||||||||||||||||||||||||
| Platform.runLater(() -> messages.add(m)) | ||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| package com.example; | ||
| import java.io.FileNotFoundException; | ||
| import java.nio.file.Path; | ||
| import java.util.function.Consumer; | ||
|
|
||
| public interface NtfyConnection { | ||
| boolean send (String message); | ||
|
|
||
| void receive (Consumer<NtfyMessageDto> messageHandler); | ||
|
|
||
| boolean sendFile(Path filePath) throws FileNotFoundException; | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,95 @@ | ||||||||||||||||||||||||
| package com.example; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| import com.fasterxml.jackson.core.JsonProcessingException; | ||||||||||||||||||||||||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||||||||||||||||||||||||
| import io.github.cdimascio.dotenv.Dotenv; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| import java.io.File; | ||||||||||||||||||||||||
| import java.io.FileNotFoundException; | ||||||||||||||||||||||||
| import java.io.IOException; | ||||||||||||||||||||||||
| import java.net.URI; | ||||||||||||||||||||||||
| import java.net.http.HttpClient; | ||||||||||||||||||||||||
| import java.net.http.HttpRequest; | ||||||||||||||||||||||||
| import java.net.http.HttpResponse; | ||||||||||||||||||||||||
| import java.nio.file.Files; | ||||||||||||||||||||||||
| import java.nio.file.Path; | ||||||||||||||||||||||||
| import java.util.UUID; | ||||||||||||||||||||||||
| import java.util.function.Consumer; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| public class NtfyConnectionImpl implements NtfyConnection { | ||||||||||||||||||||||||
| private final HttpClient http = HttpClient.newHttpClient(); | ||||||||||||||||||||||||
| private final String hostName; | ||||||||||||||||||||||||
| private final ObjectMapper mapper = new ObjectMapper(); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| public NtfyConnectionImpl() { | ||||||||||||||||||||||||
| Dotenv dotenv = Dotenv.load(); | ||||||||||||||||||||||||
| String host = dotenv.get("HOST_NAME"); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if (host == null || host.isEmpty()) { | ||||||||||||||||||||||||
| throw new IllegalStateException("HOST_NAME has to be defined"); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| this.hostName = host; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| public NtfyConnectionImpl(String hostName) { | ||||||||||||||||||||||||
| this.hostName = hostName; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||
| public boolean send (String message) { | ||||||||||||||||||||||||
| HttpRequest request = HttpRequest.newBuilder() | ||||||||||||||||||||||||
| .uri(URI.create(hostName + "/mytopic")) | ||||||||||||||||||||||||
| .POST(HttpRequest.BodyPublishers.ofString(message)) | ||||||||||||||||||||||||
| .build(); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||
| http.send(request, HttpResponse.BodyHandlers.discarding()); | ||||||||||||||||||||||||
| return true; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| } catch (IOException | InterruptedException e) { | ||||||||||||||||||||||||
| System.out.println("Error sending message!"); | ||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
Comment on lines
+49
to
+52
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. Restore interrupt flag when catching Catching } catch (IOException | InterruptedException e) {
+ if (e instanceof InterruptedException) {
+ Thread.currentThread().interrupt();
+ }
System.out.println("Error sending message!");
return false;
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||
| public boolean sendFile(Path filePath) throws FileNotFoundException { | ||||||||||||||||||||||||
| if (!Files.exists(filePath)) { | ||||||||||||||||||||||||
| throw new FileNotFoundException("File does not exist: " + filePath); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| HttpRequest httpRequest = HttpRequest.newBuilder() | ||||||||||||||||||||||||
| .uri(URI.create(hostName + "/mytopic")) | ||||||||||||||||||||||||
| .header("Content-type", "application/octet-stream") | ||||||||||||||||||||||||
| .POST(HttpRequest.BodyPublishers.ofFile(filePath)) | ||||||||||||||||||||||||
| .build(); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||
| http.send(httpRequest, HttpResponse.BodyHandlers.discarding()); | ||||||||||||||||||||||||
| return true; | ||||||||||||||||||||||||
| } catch (IOException | InterruptedException e) { | ||||||||||||||||||||||||
| System.out.println("Error sending file: " + e.getMessage()); | ||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||
| public void receive (Consumer<NtfyMessageDto> handler) { | ||||||||||||||||||||||||
| HttpRequest httpRequest = HttpRequest.newBuilder() | ||||||||||||||||||||||||
| .uri(URI.create(hostName + "/mytopic/json")) | ||||||||||||||||||||||||
| .GET() | ||||||||||||||||||||||||
| .build(); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| http.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofLines()) | ||||||||||||||||||||||||
| .thenAccept(response -> response.body() | ||||||||||||||||||||||||
| .map(line -> { | ||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||
| return mapper.readValue(line, NtfyMessageDto.class); | ||||||||||||||||||||||||
| } catch (JsonProcessingException e) { | ||||||||||||||||||||||||
| return null; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||
| .filter(msg -> msg != null && msg.event().equals("message")) | ||||||||||||||||||||||||
| .forEach(handler)); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| package com.example; | ||
|
|
||
| import com.fasterxml.jackson.annotation.JsonIgnoreProperties; | ||
|
|
||
| @JsonIgnoreProperties(ignoreUnknown = true) | ||
| public record NtfyMessageDto(String id, long time, String event, String topic, String message) { | ||
| public record Attachment(String name, String type, long size, String url) { | ||
| } | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| package com.example; | ||
|
|
||
| public class Singelton { | ||
| private final static Singelton instance = new Singelton(); | ||
|
|
||
| private Singelton() {} | ||
|
|
||
| public static Singelton getInstance() { | ||
| return instance; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,10 @@ | ||
| module hellofx { | ||
| requires javafx.controls; | ||
| requires javafx.fxml; | ||
| requires java.net.http; | ||
| requires com.fasterxml.jackson.databind; | ||
| requires java.dotenv; | ||
|
|
||
| opens com.example to javafx.fxml; | ||
| opens com.example to javafx.fxml, com.fasterxml.jackson.databind; | ||
| exports com.example; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,20 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <?import javafx.scene.layout.StackPane?> | ||
| <?import javafx.scene.control.Label?> | ||
|
|
||
| <StackPane xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml" fx:controller="com.example.HelloController"> | ||
| <children> | ||
| <Label fx:id="messageLabel" text="Hello, JavaFX!" /> | ||
| </children> | ||
| </StackPane> | ||
|
|
||
| <?import javafx.geometry.*?> | ||
| <?import javafx.scene.control.*?> | ||
| <?import javafx.scene.layout.*?> | ||
|
|
||
| <VBox spacing="10" style="-fx-background-color: GRAY;" xmlns="http://javafx.com/javafx/17.0.12" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.HelloController"> | ||
|
|
||
| <padding> | ||
| <Insets bottom="10" left="10" right="10" top="10" /> | ||
| </padding> | ||
|
|
||
| <TextArea fx:id="chatArea" editable="false" wrapText="true" prefHeight="400" /> | ||
|
|
||
| <HBox spacing="5"> | ||
| <TextArea fx:id="inputField" prefRowCount="2" promptText="Skriv meddelande..." style="-fx-background-color: WHITE;" wrapText="true" HBox.hgrow="ALWAYS" /> | ||
| <Button fx:id="sendButton" text="Skicka" /> | ||
| <Button fx:id="sendFileButton" text="Bifoga fil"/> | ||
| </HBox> | ||
| </VBox> |
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.
Missing feedback when file send fails.
model.sendFile(file)can fail silently (returnsfalseor throws). The user sees "Du skickade fil:" regardless of outcome. Consider checking the result and providing appropriate feedback.@FXML private void attachFile() { FileChooser chooser = new FileChooser(); chooser.setTitle("Välj fil att bifoga"); File file = chooser.showOpenDialog(sendFileButton.getScene().getWindow()); if (file != null) { - model.sendFile(file); - chatArea.appendText("Du skickade fil: " + file.getName() + "\n"); + boolean success = model.sendFile(file); + if (success) { + chatArea.appendText("Du skickade fil: " + file.getName() + "\n"); + } else { + chatArea.appendText("Kunde inte skicka fil: " + file.getName() + "\n"); + } } }Note: This requires changing
HelloModel.sendFile()to returnboolean.🤖 Prompt for AI Agents