Defer Chat Attachments
This commit is contained in:
@@ -1,50 +0,0 @@
|
||||
package com.petshop.backend.controller;
|
||||
|
||||
import com.petshop.backend.service.ChatAttachmentService;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/chat/attachments")
|
||||
public class ChatAttachmentController {
|
||||
|
||||
private final ChatAttachmentService attachmentService;
|
||||
|
||||
public ChatAttachmentController(ChatAttachmentService attachmentService) {
|
||||
this.attachmentService = attachmentService;
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')")
|
||||
public ResponseEntity<?> uploadAttachment(@RequestParam("file") MultipartFile file) {
|
||||
if (file.isEmpty()) {
|
||||
return ResponseEntity.badRequest().body(Map.of("message", "File is empty"));
|
||||
}
|
||||
try {
|
||||
return ResponseEntity.ok(attachmentService.storeAttachment(file));
|
||||
} catch (IOException e) {
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body(Map.of("message", "Failed to store attachment: " + e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/{filename}")
|
||||
@PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')")
|
||||
public ResponseEntity<Resource> getAttachment(@PathVariable String filename) {
|
||||
try {
|
||||
Resource resource = attachmentService.loadAttachmentResource(filename);
|
||||
return ResponseEntity.ok()
|
||||
.contentType(attachmentService.resolveMediaType(filename))
|
||||
.body(resource);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,6 @@ package com.petshop.backend.dto.chat;
|
||||
|
||||
public class MessageRequest {
|
||||
private String content;
|
||||
private String attachmentUrl;
|
||||
private String attachmentName;
|
||||
private String attachmentMimeType;
|
||||
private Long attachmentSizeBytes;
|
||||
|
||||
public MessageRequest() {
|
||||
}
|
||||
@@ -17,36 +13,4 @@ public class MessageRequest {
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public String getAttachmentUrl() {
|
||||
return attachmentUrl;
|
||||
}
|
||||
|
||||
public void setAttachmentUrl(String attachmentUrl) {
|
||||
this.attachmentUrl = attachmentUrl;
|
||||
}
|
||||
|
||||
public String getAttachmentName() {
|
||||
return attachmentName;
|
||||
}
|
||||
|
||||
public void setAttachmentName(String attachmentName) {
|
||||
this.attachmentName = attachmentName;
|
||||
}
|
||||
|
||||
public String getAttachmentMimeType() {
|
||||
return attachmentMimeType;
|
||||
}
|
||||
|
||||
public void setAttachmentMimeType(String attachmentMimeType) {
|
||||
this.attachmentMimeType = attachmentMimeType;
|
||||
}
|
||||
|
||||
public Long getAttachmentSizeBytes() {
|
||||
return attachmentSizeBytes;
|
||||
}
|
||||
|
||||
public void setAttachmentSizeBytes(Long attachmentSizeBytes) {
|
||||
this.attachmentSizeBytes = attachmentSizeBytes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,10 +12,6 @@ public class MessageResponse {
|
||||
private String content;
|
||||
private LocalDateTime timestamp;
|
||||
private Boolean isRead;
|
||||
private String attachmentUrl;
|
||||
private String attachmentName;
|
||||
private String attachmentMimeType;
|
||||
private Long attachmentSizeBytes;
|
||||
|
||||
public MessageResponse() {
|
||||
}
|
||||
@@ -38,15 +34,6 @@ public class MessageResponse {
|
||||
response.setContent(message.getContent());
|
||||
response.setTimestamp(message.getTimestamp());
|
||||
response.setIsRead(message.getIsRead());
|
||||
|
||||
|
||||
if (message.getAttachmentUrl() != null) {
|
||||
response.setAttachmentUrl("/api/v1/chat/messages/" + message.getId() + "/attachment");
|
||||
}
|
||||
|
||||
response.setAttachmentName(message.getAttachmentName());
|
||||
response.setAttachmentMimeType(message.getAttachmentMimeType());
|
||||
response.setAttachmentSizeBytes(message.getAttachmentSizeBytes());
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -105,36 +92,4 @@ public class MessageResponse {
|
||||
public void setIsRead(Boolean isRead) {
|
||||
this.isRead = isRead;
|
||||
}
|
||||
|
||||
public String getAttachmentUrl() {
|
||||
return attachmentUrl;
|
||||
}
|
||||
|
||||
public void setAttachmentUrl(String attachmentUrl) {
|
||||
this.attachmentUrl = attachmentUrl;
|
||||
}
|
||||
|
||||
public String getAttachmentName() {
|
||||
return attachmentName;
|
||||
}
|
||||
|
||||
public void setAttachmentName(String attachmentName) {
|
||||
this.attachmentName = attachmentName;
|
||||
}
|
||||
|
||||
public String getAttachmentMimeType() {
|
||||
return attachmentMimeType;
|
||||
}
|
||||
|
||||
public void setAttachmentMimeType(String attachmentMimeType) {
|
||||
this.attachmentMimeType = attachmentMimeType;
|
||||
}
|
||||
|
||||
public Long getAttachmentSizeBytes() {
|
||||
return attachmentSizeBytes;
|
||||
}
|
||||
|
||||
public void setAttachmentSizeBytes(Long attachmentSizeBytes) {
|
||||
this.attachmentSizeBytes = attachmentSizeBytes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,17 +22,6 @@ public class Message {
|
||||
@Column(columnDefinition = "TEXT")
|
||||
private String content;
|
||||
|
||||
@Column(length = 255)
|
||||
private String attachmentUrl;
|
||||
|
||||
@Column(length = 255)
|
||||
private String attachmentName;
|
||||
|
||||
@Column(length = 100)
|
||||
private String attachmentMimeType;
|
||||
|
||||
private Long attachmentSizeBytes;
|
||||
|
||||
@CreationTimestamp
|
||||
@Column(nullable = false, updatable = false)
|
||||
private LocalDateTime timestamp;
|
||||
@@ -99,36 +88,4 @@ public class Message {
|
||||
public void setIsRead(Boolean isRead) {
|
||||
this.isRead = isRead;
|
||||
}
|
||||
|
||||
public String getAttachmentUrl() {
|
||||
return attachmentUrl;
|
||||
}
|
||||
|
||||
public void setAttachmentUrl(String attachmentUrl) {
|
||||
this.attachmentUrl = attachmentUrl;
|
||||
}
|
||||
|
||||
public String getAttachmentName() {
|
||||
return attachmentName;
|
||||
}
|
||||
|
||||
public void setAttachmentName(String attachmentName) {
|
||||
this.attachmentName = attachmentName;
|
||||
}
|
||||
|
||||
public String getAttachmentMimeType() {
|
||||
return attachmentMimeType;
|
||||
}
|
||||
|
||||
public void setAttachmentMimeType(String attachmentMimeType) {
|
||||
this.attachmentMimeType = attachmentMimeType;
|
||||
}
|
||||
|
||||
public Long getAttachmentSizeBytes() {
|
||||
return attachmentSizeBytes;
|
||||
}
|
||||
|
||||
public void setAttachmentSizeBytes(Long attachmentSizeBytes) {
|
||||
this.attachmentSizeBytes = attachmentSizeBytes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
package com.petshop.backend.service;
|
||||
|
||||
import org.springframework.core.io.PathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.MediaTypeFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
public class ChatAttachmentService {
|
||||
|
||||
private static final String STORED_PREFIX = "/uploads/chat/";
|
||||
private final Path chatDirectory = Paths.get("uploads", "chat").toAbsolutePath().normalize();
|
||||
|
||||
public record AttachmentMetadata(String url, String fileName, String mimeType, long size) {}
|
||||
|
||||
public AttachmentMetadata storeAttachment(MultipartFile file) throws IOException {
|
||||
Files.createDirectories(chatDirectory);
|
||||
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
String extension = resolveExtension(originalFilename);
|
||||
String filename = UUID.randomUUID() + extension;
|
||||
Path filePath = chatDirectory.resolve(filename).normalize();
|
||||
|
||||
Files.copy(file.getInputStream(), filePath, StandardCopyOption.REPLACE_EXISTING);
|
||||
|
||||
String url = "/api/v1/chat/attachments/" + filename;
|
||||
return new AttachmentMetadata(url, originalFilename, file.getContentType(), file.getSize());
|
||||
}
|
||||
|
||||
public Resource loadAttachmentResource(String filename) {
|
||||
Path filePath = chatDirectory.resolve(filename).normalize();
|
||||
if (!filePath.startsWith(chatDirectory) || !Files.exists(filePath) || !Files.isRegularFile(filePath)) {
|
||||
throw new IllegalArgumentException("Attachment file was not found");
|
||||
}
|
||||
return new PathResource(filePath);
|
||||
}
|
||||
|
||||
public MediaType resolveMediaType(String filename) {
|
||||
return MediaTypeFactory.getMediaType(filename).orElse(MediaType.APPLICATION_OCTET_STREAM);
|
||||
}
|
||||
|
||||
private String resolveExtension(String originalFilename) {
|
||||
if (originalFilename == null) return "";
|
||||
int idx = originalFilename.lastIndexOf('.');
|
||||
return idx < 0 ? "" : originalFilename.substring(idx);
|
||||
}
|
||||
}
|
||||
@@ -131,10 +131,6 @@ public class ChatService {
|
||||
message.setConversationId(conversationId);
|
||||
message.setSenderId(userId);
|
||||
message.setContent(request.getContent());
|
||||
message.setAttachmentUrl(request.getAttachmentUrl());
|
||||
message.setAttachmentName(request.getAttachmentName());
|
||||
message.setAttachmentMimeType(request.getAttachmentMimeType());
|
||||
message.setAttachmentSizeBytes(request.getAttachmentSizeBytes());
|
||||
message.setIsRead(false);
|
||||
message = messageRepository.save(message);
|
||||
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
package org.example.petshopdesktop.api.dto.chat;
|
||||
|
||||
public class ChatAttachmentResponse {
|
||||
private String url;
|
||||
private String fileName;
|
||||
private String mimeType;
|
||||
private long size;
|
||||
|
||||
public ChatAttachmentResponse() {
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
public String getMimeType() {
|
||||
return mimeType;
|
||||
}
|
||||
|
||||
public void setMimeType(String mimeType) {
|
||||
this.mimeType = mimeType;
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public void setSize(long size) {
|
||||
this.size = size;
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,6 @@ package org.example.petshopdesktop.api.dto.chat;
|
||||
|
||||
public class MessageRequest {
|
||||
private String content;
|
||||
private String attachmentUrl;
|
||||
private String attachmentName;
|
||||
private String attachmentMimeType;
|
||||
private Long attachmentSizeBytes;
|
||||
|
||||
public MessageRequest() {
|
||||
}
|
||||
@@ -21,36 +17,4 @@ public class MessageRequest {
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public String getAttachmentUrl() {
|
||||
return attachmentUrl;
|
||||
}
|
||||
|
||||
public void setAttachmentUrl(String attachmentUrl) {
|
||||
this.attachmentUrl = attachmentUrl;
|
||||
}
|
||||
|
||||
public String getAttachmentName() {
|
||||
return attachmentName;
|
||||
}
|
||||
|
||||
public void setAttachmentName(String attachmentName) {
|
||||
this.attachmentName = attachmentName;
|
||||
}
|
||||
|
||||
public String getAttachmentMimeType() {
|
||||
return attachmentMimeType;
|
||||
}
|
||||
|
||||
public void setAttachmentMimeType(String attachmentMimeType) {
|
||||
this.attachmentMimeType = attachmentMimeType;
|
||||
}
|
||||
|
||||
public Long getAttachmentSizeBytes() {
|
||||
return attachmentSizeBytes;
|
||||
}
|
||||
|
||||
public void setAttachmentSizeBytes(Long attachmentSizeBytes) {
|
||||
this.attachmentSizeBytes = attachmentSizeBytes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,10 +10,6 @@ public class MessageResponse {
|
||||
private String content;
|
||||
private LocalDateTime timestamp;
|
||||
private Boolean isRead;
|
||||
private String attachmentUrl;
|
||||
private String attachmentName;
|
||||
private String attachmentMimeType;
|
||||
private Long attachmentSizeBytes;
|
||||
|
||||
public MessageResponse() {
|
||||
}
|
||||
@@ -73,36 +69,4 @@ public class MessageResponse {
|
||||
public void setIsRead(Boolean isRead) {
|
||||
this.isRead = isRead;
|
||||
}
|
||||
|
||||
public String getAttachmentUrl() {
|
||||
return attachmentUrl;
|
||||
}
|
||||
|
||||
public void setAttachmentUrl(String attachmentUrl) {
|
||||
this.attachmentUrl = attachmentUrl;
|
||||
}
|
||||
|
||||
public String getAttachmentName() {
|
||||
return attachmentName;
|
||||
}
|
||||
|
||||
public void setAttachmentName(String attachmentName) {
|
||||
this.attachmentName = attachmentName;
|
||||
}
|
||||
|
||||
public String getAttachmentMimeType() {
|
||||
return attachmentMimeType;
|
||||
}
|
||||
|
||||
public void setAttachmentMimeType(String attachmentMimeType) {
|
||||
this.attachmentMimeType = attachmentMimeType;
|
||||
}
|
||||
|
||||
public Long getAttachmentSizeBytes() {
|
||||
return attachmentSizeBytes;
|
||||
}
|
||||
|
||||
public void setAttachmentSizeBytes(Long attachmentSizeBytes) {
|
||||
this.attachmentSizeBytes = attachmentSizeBytes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,10 @@ package org.example.petshopdesktop.api.endpoints;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import org.example.petshopdesktop.api.ApiClient;
|
||||
import org.example.petshopdesktop.api.dto.chat.ChatAttachmentResponse;
|
||||
import org.example.petshopdesktop.api.dto.chat.ConversationRequest;
|
||||
import org.example.petshopdesktop.api.dto.chat.ConversationResponse;
|
||||
import org.example.petshopdesktop.api.dto.chat.MessageRequest;
|
||||
import org.example.petshopdesktop.api.dto.chat.MessageResponse;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
public class ChatApi {
|
||||
@@ -44,8 +41,4 @@ public class ChatApi {
|
||||
public MessageResponse sendMessage(Long conversationId, MessageRequest request) throws Exception {
|
||||
return apiClient.post("/api/v1/chat/conversations/" + conversationId + "/messages", request, MessageResponse.class);
|
||||
}
|
||||
|
||||
public ChatAttachmentResponse uploadAttachment(Path filePath) throws Exception {
|
||||
return apiClient.postMultipart("/api/v1/chat/attachments", "file", filePath, ChatAttachmentResponse.class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import javafx.scene.paint.ImagePattern;
|
||||
import javafx.scene.shape.Circle;
|
||||
import javafx.scene.image.Image;
|
||||
import org.example.petshopdesktop.api.ChatRealtimeClient;
|
||||
import org.example.petshopdesktop.api.dto.chat.ChatAttachmentResponse;
|
||||
import org.example.petshopdesktop.api.dto.chat.ConversationResponse;
|
||||
import org.example.petshopdesktop.api.dto.chat.MessageRequest;
|
||||
import org.example.petshopdesktop.api.dto.chat.MessageResponse;
|
||||
@@ -30,6 +29,7 @@ import org.example.petshopdesktop.api.endpoints.DropdownApi;
|
||||
import org.example.petshopdesktop.auth.UserSession;
|
||||
import org.example.petshopdesktop.util.ActivityLogger;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
@@ -71,7 +71,7 @@ public class ChatController {
|
||||
private final Map<Long, String> customerLabels = new HashMap<>();
|
||||
private final ChatRealtimeClient realtimeClient = ChatRealtimeClient.getInstance();
|
||||
private ConversationResponse selectedConversation;
|
||||
private ChatAttachmentResponse selectedAttachment;
|
||||
private File selectedAttachmentFile;
|
||||
|
||||
@FXML
|
||||
public void initialize() {
|
||||
@@ -155,42 +155,38 @@ public class ChatController {
|
||||
}
|
||||
|
||||
String content = txtMessage.getText() == null ? "" : txtMessage.getText().trim();
|
||||
if (content.isEmpty() && selectedAttachment == null) {
|
||||
if (content.isEmpty()) {
|
||||
if (selectedAttachmentFile != null) {
|
||||
lblChatStatus.setText("Attachments are not available yet");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Long convId = selectedConversation.getId();
|
||||
var attachment = selectedAttachment;
|
||||
|
||||
txtMessage.clear();
|
||||
btnSend.setDisable(true);
|
||||
selectedAttachment = null;
|
||||
btnAttachment.setText("📎");
|
||||
btnAttachment.setStyle("-fx-background-color: #e2e8f0; -fx-background-radius: 12; -fx-text-fill: #475569; -fx-cursor: hand;");
|
||||
|
||||
lblChatStatus.setText("Sending message...");
|
||||
|
||||
new Thread(() -> {
|
||||
try {
|
||||
MessageRequest request = new MessageRequest(content);
|
||||
if (attachment != null) {
|
||||
request.setAttachmentUrl(attachment.getUrl());
|
||||
request.setAttachmentName(attachment.getFileName());
|
||||
request.setAttachmentMimeType(attachment.getMimeType());
|
||||
request.setAttachmentSizeBytes(attachment.getSize());
|
||||
}
|
||||
MessageResponse response = ChatApi.getInstance().sendMessage(convId, request);
|
||||
Platform.runLater(() -> {
|
||||
btnSend.setDisable(false);
|
||||
appendMessageIfSelected(response);
|
||||
lblChatStatus.setText("Message sent");
|
||||
if (selectedAttachmentFile != null) {
|
||||
clearLocalAttachment();
|
||||
lblChatStatus.setText("Message sent without attachment");
|
||||
} else {
|
||||
lblChatStatus.setText("Message sent");
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Platform.runLater(() -> {
|
||||
txtMessage.setText(content);
|
||||
btnSend.setDisable(false);
|
||||
selectedAttachment = attachment;
|
||||
btnAttachment.setText("📎 " + attachment.getFileName());
|
||||
lblChatStatus.setText("Chat send failed");
|
||||
ActivityLogger.getInstance().logException(
|
||||
"ChatController.sendMessage",
|
||||
@@ -203,30 +199,19 @@ public class ChatController {
|
||||
|
||||
@FXML
|
||||
void btnAttachmentClicked(ActionEvent event) {
|
||||
java.io.File file = org.example.petshopdesktop.util.FilePickerSupport.pickAnyFile(btnAttachment.getScene().getWindow());
|
||||
File file = org.example.petshopdesktop.util.FilePickerSupport.pickAnyFile(btnAttachment.getScene().getWindow());
|
||||
if (file == null) return;
|
||||
|
||||
btnAttachment.setDisable(true);
|
||||
lblChatStatus.setText("Uploading attachment...");
|
||||
selectedAttachmentFile = file;
|
||||
btnAttachment.setText("📎 " + file.getName());
|
||||
btnAttachment.setStyle("-fx-background-color: #dcfce7; -fx-background-radius: 12; -fx-text-fill: #166534; -fx-cursor: hand;");
|
||||
lblChatStatus.setText("Attachment selected");
|
||||
}
|
||||
|
||||
new Thread(() -> {
|
||||
try {
|
||||
var response = ChatApi.getInstance().uploadAttachment(file.toPath());
|
||||
Platform.runLater(() -> {
|
||||
selectedAttachment = response;
|
||||
btnAttachment.setText("📎 " + response.getFileName());
|
||||
btnAttachment.setStyle("-fx-background-color: #dcfce7; -fx-background-radius: 12; -fx-text-fill: #166534; -fx-cursor: hand;");
|
||||
lblChatStatus.setText("File ready to send");
|
||||
btnAttachment.setDisable(false);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
ActivityLogger.getInstance().logException("ChatController.btnAttachmentClicked", e, "Uploading chat attachment");
|
||||
Platform.runLater(() -> {
|
||||
lblChatStatus.setText("Upload failed");
|
||||
btnAttachment.setDisable(false);
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
private void clearLocalAttachment() {
|
||||
selectedAttachmentFile = null;
|
||||
btnAttachment.setText("📎");
|
||||
btnAttachment.setStyle("-fx-background-color: #e2e8f0; -fx-background-radius: 12; -fx-text-fill: #475569; -fx-cursor: hand;");
|
||||
}
|
||||
|
||||
private void loadCustomers() {
|
||||
@@ -397,26 +382,6 @@ public class ChatController {
|
||||
bubble.getChildren().add(content);
|
||||
}
|
||||
|
||||
if (message.getAttachmentUrl() != null && !message.getAttachmentUrl().isBlank()) {
|
||||
String attachmentLabel = "📎 " + (message.getAttachmentName() == null || message.getAttachmentName().isBlank() ? "Attachment" : message.getAttachmentName());
|
||||
if (message.getAttachmentSizeBytes() != null && message.getAttachmentSizeBytes() > 0) {
|
||||
attachmentLabel = attachmentLabel + " (" + formatSize(message.getAttachmentSizeBytes()) + ")";
|
||||
}
|
||||
Button attachmentBtn = new Button(attachmentLabel);
|
||||
attachmentBtn.setStyle("-fx-background-color: " + (mine ? "rgba(255,255,255,0.15)" : "rgba(0,0,0,0.05)") + "; -fx-text-fill: " + (mine ? "#ffffff" : "#0f766e") + "; -fx-cursor: hand; -fx-background-radius: 8; -fx-padding: 6 10;");
|
||||
attachmentBtn.setOnAction(e -> {
|
||||
try {
|
||||
String fullUrl = org.example.petshopdesktop.api.ApiConfig.getInstance().getBaseUrl() + message.getAttachmentUrl();
|
||||
if (java.awt.Desktop.isDesktopSupported()) {
|
||||
java.awt.Desktop.getDesktop().browse(new java.net.URI(fullUrl));
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
ActivityLogger.getInstance().logException("ChatController.attachmentOpen", ex, "Opening attachment URL");
|
||||
}
|
||||
});
|
||||
bubble.getChildren().add(attachmentBtn);
|
||||
}
|
||||
|
||||
bubble.getChildren().add(timestamp);
|
||||
bubble.setMaxWidth(420);
|
||||
bubble.setStyle(mine
|
||||
@@ -475,17 +440,4 @@ public class ChatController {
|
||||
private void scrollMessagesToBottom() {
|
||||
Platform.runLater(() -> spMessages.setVvalue(1.0));
|
||||
}
|
||||
private String formatSize(Long bytes) {
|
||||
if (bytes == null || bytes <= 0) {
|
||||
return "";
|
||||
}
|
||||
double size = bytes;
|
||||
String[] units = {"B", "KB", "MB", "GB"};
|
||||
int unitIndex = 0;
|
||||
while (size >= 1024 && unitIndex < units.length - 1) {
|
||||
size = size / 1024;
|
||||
unitIndex++;
|
||||
}
|
||||
return unitIndex == 0 ? String.format("%.0f %s", size, units[unitIndex]) : String.format("%.1f %s", size, units[unitIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user