Skip to content

Commit ac2dccc

Browse files
committed
Final push...
1 parent 6ea1311 commit ac2dccc

5 files changed

Lines changed: 321 additions & 334 deletions

File tree

javafx.iml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<module version="4">
3+
<component name="AdditionalModuleElements">
4+
<content url="file://$MODULE_DIR$" dumb="true">
5+
<sourceFolder url="file://$MODULE_DIR$/src/test" isTestSource="true" />
6+
</content>
7+
</component>
8+
</module>
Lines changed: 14 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,99 +1,34 @@
11
package com.example;
22

3-
/**
4-
* Immutable chat message value object.
5-
*
6-
* Fields:
7-
* - id: optional ntfy message id (may be null)
8-
* - username: sender (never null; use "Anonymous" if unknown)
9-
* - message: text payload (may be empty)
10-
* - timestamp: formatted timestamp string (never null)
11-
* - fileName: optional attached file name (may be null)
12-
* - fileData: optional Base64 file data (may be null)
13-
*/
143
public class ChatMessage {
154
private final String id;
165
private final String username;
176
private final String message;
187
private final String timestamp;
198
private final String fileName;
20-
private final String fileData;
9+
private final String fileUrl;
10+
private final String mimeType;
2111

22-
/**
23-
* Full constructor.
24-
*/
2512
public ChatMessage(String id, String username, String message, String timestamp,
26-
String fileName, String fileData) {
13+
String fileName, String fileUrl, String mimeType) {
2714
this.id = id;
28-
this.username = username == null ? "Anonymous" : username;
29-
this.message = message == null ? "" : message;
30-
this.timestamp = timestamp == null ? "" : timestamp;
15+
this.username = username;
16+
this.message = message;
17+
this.timestamp = timestamp;
3118
this.fileName = fileName;
32-
this.fileData = fileData;
19+
this.fileUrl = fileUrl;
20+
this.mimeType = mimeType;
3321
}
3422

35-
/**
36-
* Constructor for file messages (no fileData).
37-
*/
38-
public ChatMessage(String id, String username, String message, String timestamp, String fileName) {
39-
this(id, username, message, timestamp, fileName, null);
40-
}
41-
42-
/**
43-
* Constructor for normal messages (no file).
44-
*/
4523
public ChatMessage(String id, String username, String message, String timestamp) {
46-
this(id, username, message, timestamp, null, null);
47-
}
48-
49-
/**
50-
* Convenience constructor when ID is not known.
51-
*/
52-
public ChatMessage(String username, String message, String timestamp) {
53-
this(null, username, message, timestamp, null, null);
54-
}
55-
56-
// Getters
57-
public String getId() {
58-
return id;
24+
this(id, username, message, timestamp, null, null, null);
5925
}
6026

61-
public String getUsername() {
62-
return username;
63-
}
64-
65-
public String getMessage() {
66-
return message;
67-
}
68-
69-
public String getTimestamp() {
70-
return timestamp;
71-
}
72-
73-
public String getFileName() {
74-
return fileName;
75-
}
76-
77-
public String getFileData() {
78-
return fileData;
79-
}
80-
81-
private String fileUrl;
82-
private String mimeType;
83-
27+
public String getId() { return id; }
28+
public String getUsername() { return username; }
29+
public String getMessage() { return message; }
30+
public String getTimestamp() { return timestamp; }
31+
public String getFileName() { return fileName; }
8432
public String getFileUrl() { return fileUrl; }
85-
public void setFileUrl(String url) { this.fileUrl = url; }
86-
8733
public String getMimeType() { return mimeType; }
88-
public void setMimeType(String t) { this.mimeType = t; }
89-
90-
91-
@Override
92-
public String toString() {
93-
if (fileName != null) {
94-
return username + " (" + timestamp + ")\n" + message + " [" + fileName + "]";
95-
} else {
96-
return username + " (" + timestamp + ")\n" + message;
97-
}
98-
}
9934
}

src/main/java/com/example/HelloController.java

Lines changed: 25 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,13 @@
1313
import javafx.scene.control.TextField;
1414
import javafx.scene.image.Image;
1515
import javafx.scene.image.ImageView;
16-
import javafx.scene.layout.VBox;
1716
import javafx.scene.text.Text;
1817
import javafx.scene.text.TextFlow;
1918
import javafx.stage.FileChooser;
2019

2120
import java.io.File;
2221
import java.net.URL;
23-
import java.nio.file.Files;
2422
import java.time.format.DateTimeFormatter;
25-
import java.util.Base64;
2623
import java.util.ResourceBundle;
2724
import java.util.function.Predicate;
2825

@@ -46,99 +43,48 @@ private String getCurrentUsername() {
4643

4744
@Override
4845
public void initialize(URL url, ResourceBundle rb) {
49-
5046
filteredList = new FilteredList<>(masterList, msg -> true);
5147
chatList.setItems(filteredList);
5248

5349
chatList.setCellFactory(list -> new ListCell<>() {
5450
@Override
5551
protected void updateItem(ChatMessage msg, boolean empty) {
5652
super.updateItem(msg, empty);
57-
if (empty || msg == null) {
58-
setGraphic(null);
59-
return;
60-
}
53+
if (empty || msg == null) { setGraphic(null); return; }
6154

6255
Text user = new Text(msg.getUsername());
6356
user.setStyle("-fx-font-weight: bold;");
64-
6557
Text time = new Text(" (" + msg.getTimestamp() + ")\n");
6658
time.setStyle("-fx-fill: gray; -fx-font-size: 12px;");
6759

68-
if (msg.getFileName() != null) {
69-
70-
if (msg.getFileUrl() != null && msg.getMimeType() != null) {
71-
72-
// Images → inline display
73-
if (msg.getMimeType().startsWith("image/")) {
74-
ImageView img = new ImageView(new Image(msg.getFileUrl(), true));
75-
img.setPreserveRatio(true);
76-
img.setFitWidth(250);
77-
78-
VBox box = new VBox(
79-
new TextFlow(user, time),
80-
img
81-
);
82-
setGraphic(box);
83-
return;
84-
}
85-
60+
if (msg.getFileName() != null && msg.getFileUrl() != null) {
61+
if (msg.getMimeType() != null && msg.getMimeType().startsWith("image/")) {
62+
try {
63+
ImageView imageView = new ImageView(new Image(msg.getFileUrl(), true));
64+
imageView.setFitWidth(200);
65+
imageView.setPreserveRatio(true);
66+
Text messageText = new Text(msg.getMessage() + "\n");
67+
messageText.setStyle("-fx-font-size: 14px;");
68+
setGraphic(new TextFlow(user, time, messageText, imageView));
69+
} catch (Exception e) { e.printStackTrace(); }
70+
} else {
8671
Hyperlink link = new Hyperlink(msg.getFileName());
87-
link.setOnAction(e -> {
88-
try {
89-
HelloFX.hostServices().showDocument(msg.getFileUrl());
90-
} catch (Exception ex) {
91-
ex.printStackTrace();
92-
}
93-
});
94-
95-
VBox box = new VBox(
96-
new TextFlow(user, time),
97-
link
98-
);
99-
setGraphic(box);
100-
return;
101-
}
102-
103-
104-
if (msg.getFileData() != null) {
105-
Text messageText = new Text((msg.getMessage() == null ? "" : msg.getMessage()) + "\n");
72+
final String fileUrl = msg.getFileUrl();
73+
link.setOnAction(ev -> HelloFX.hostServices().showDocument(fileUrl));
74+
Text messageText = new Text(msg.getMessage() + "\n");
10675
messageText.setStyle("-fx-font-size: 14px;");
107-
108-
Hyperlink fileLink = new Hyperlink(msg.getFileName());
109-
final String fileName = msg.getFileName();
110-
final String fileData = msg.getFileData();
111-
112-
fileLink.setOnAction(event -> {
113-
try {
114-
byte[] data = Base64.getDecoder().decode(fileData);
115-
FileChooser chooser = new FileChooser();
116-
chooser.setInitialFileName(fileName);
117-
File saveFile = chooser.showSaveDialog(chatList.getScene().getWindow());
118-
if (saveFile != null) {
119-
Files.write(saveFile.toPath(), data);
120-
}
121-
} catch (Exception e) {
122-
e.printStackTrace();
123-
}
124-
});
125-
126-
TextFlow flow = new TextFlow(user, time, messageText, fileLink);
127-
setGraphic(flow);
128-
return;
76+
setGraphic(new TextFlow(user, time, messageText, link));
12977
}
78+
} else {
79+
Text text = new Text(msg.getMessage());
80+
text.setStyle("-fx-font-size: 14px;");
81+
setGraphic(new TextFlow(user, time, text));
13082
}
131-
132-
String body = msg.getMessage() == null ? "" : msg.getMessage();
133-
Text text = new Text(body);
134-
text.setStyle("-fx-font-size: 14px;");
135-
TextFlow flow = new TextFlow(user, time, text);
136-
setGraphic(flow);
13783
}
13884
});
13985

140-
usernameField.textProperty().addListener((obs, oldVal, newVal) -> updateFilterPredicate());
14186
hideMyMessagesCheck.selectedProperty().addListener((obs, oldVal, newVal) -> updateFilterPredicate());
87+
usernameField.textProperty().addListener((obs, oldVal, newVal) -> updateFilterPredicate());
14288

14389
model.loadHistory(msg -> Platform.runLater(() -> masterList.add(msg)));
14490
model.listenForMessages(msg -> Platform.runLater(() -> masterList.add(msg)));
@@ -147,14 +93,7 @@ protected void updateItem(ChatMessage msg, boolean empty) {
14793
private void updateFilterPredicate() {
14894
final String current = getCurrentUsername();
14995
final boolean hideMine = hideMyMessagesCheck.isSelected();
150-
151-
Predicate<ChatMessage> pred = chatMessage -> {
152-
if (!hideMine) return true;
153-
String msgUser = chatMessage.getUsername();
154-
if (msgUser == null) msgUser = "Anonymous";
155-
return !msgUser.equals(current);
156-
};
157-
96+
Predicate<ChatMessage> pred = msg -> !hideMine || !current.equals(msg.getUsername());
15897
filteredList.setPredicate(pred);
15998
}
16099

@@ -163,22 +102,11 @@ private void onSend() {
163102
String user = getCurrentUsername();
164103
String msg = inputField.getText().trim();
165104
if (msg.isEmpty()) return;
166-
167-
final String safeUser = user;
168-
final String safeMsg = msg;
169105
inputField.clear();
170106

171107
new Thread(() -> {
172-
try {
173-
model.sendMessage(safeUser, safeMsg);
174-
} catch (Exception e) {
175-
e.printStackTrace();
176-
Platform.runLater(() -> masterList.add(new ChatMessage(
177-
"system",
178-
"[send failed] " + e.getMessage(),
179-
java.time.ZonedDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
180-
)));
181-
}
108+
try { model.sendMessage(user, msg); }
109+
catch (Exception e) { e.printStackTrace(); }
182110
}).start();
183111
}
184112

@@ -187,8 +115,6 @@ private void onAttachFile() {
187115
FileChooser fileChooser = new FileChooser();
188116
fileChooser.setTitle("Select a file to send");
189117
File file = fileChooser.showOpenDialog(chatList.getScene().getWindow());
190-
if (file != null) {
191-
new Thread(() -> model.sendFile(file, getCurrentUsername())).start();
192-
}
118+
if (file != null) new Thread(() -> model.sendFile(getCurrentUsername(), file)).start();
193119
}
194120
}

0 commit comments

Comments
 (0)