2020-06-08 10:36:30 +02:00
|
|
|
package envoy.client.ui.controller;
|
2020-03-26 20:23:25 +01:00
|
|
|
|
2020-06-22 22:51:10 +02:00
|
|
|
import java.awt.Toolkit;
|
|
|
|
import java.awt.datatransfer.StringSelection;
|
2020-07-10 20:53:28 +02:00
|
|
|
import java.io.ByteArrayInputStream;
|
2020-07-09 16:05:09 +02:00
|
|
|
import java.io.File;
|
2020-03-28 15:32:24 +01:00
|
|
|
import java.io.IOException;
|
2020-07-09 16:05:09 +02:00
|
|
|
import java.nio.file.Files;
|
2020-03-28 15:32:24 +01:00
|
|
|
import java.util.logging.Level;
|
2020-03-26 20:23:25 +01:00
|
|
|
import java.util.logging.Logger;
|
2020-04-02 22:03:43 +02:00
|
|
|
import java.util.stream.Collectors;
|
2020-03-26 20:23:25 +01:00
|
|
|
|
2020-04-18 19:46:04 +02:00
|
|
|
import javafx.application.Platform;
|
|
|
|
import javafx.collections.FXCollections;
|
|
|
|
import javafx.fxml.FXML;
|
|
|
|
import javafx.scene.control.*;
|
2020-06-10 13:05:44 +02:00
|
|
|
import javafx.scene.control.Alert.AlertType;
|
2020-07-10 20:53:28 +02:00
|
|
|
import javafx.scene.image.Image;
|
2020-06-26 17:33:32 +02:00
|
|
|
import javafx.scene.image.ImageView;
|
2020-04-18 19:46:04 +02:00
|
|
|
import javafx.scene.input.KeyCode;
|
|
|
|
import javafx.scene.input.KeyEvent;
|
2020-06-07 23:28:25 +02:00
|
|
|
import javafx.scene.paint.Color;
|
2020-07-09 16:05:09 +02:00
|
|
|
import javafx.stage.FileChooser;
|
2020-04-18 19:46:04 +02:00
|
|
|
|
2020-07-05 14:38:19 +02:00
|
|
|
import envoy.client.data.*;
|
2020-07-05 12:04:25 +02:00
|
|
|
import envoy.client.data.audio.AudioRecorder;
|
2020-03-29 22:15:05 +02:00
|
|
|
import envoy.client.event.MessageCreationEvent;
|
2020-03-28 15:32:24 +01:00
|
|
|
import envoy.client.net.Client;
|
|
|
|
import envoy.client.net.WriteProxy;
|
2020-06-30 21:20:54 +02:00
|
|
|
import envoy.client.ui.IconUtil;
|
2020-07-04 15:26:12 +02:00
|
|
|
import envoy.client.ui.Restorable;
|
2020-06-30 21:20:54 +02:00
|
|
|
import envoy.client.ui.SceneContext;
|
2020-07-01 19:35:15 +02:00
|
|
|
import envoy.client.ui.listcell.ContactListCellFactory;
|
|
|
|
import envoy.client.ui.listcell.MessageControl;
|
|
|
|
import envoy.client.ui.listcell.MessageListCellFactory;
|
2020-06-10 11:05:28 +02:00
|
|
|
import envoy.data.*;
|
2020-07-03 23:32:22 +02:00
|
|
|
import envoy.data.Attachment.AttachmentType;
|
2020-07-08 14:15:44 +02:00
|
|
|
import envoy.event.*;
|
2020-06-20 10:00:38 +02:00
|
|
|
import envoy.event.contact.ContactOperation;
|
2020-07-03 23:32:22 +02:00
|
|
|
import envoy.exception.EnvoyException;
|
2020-03-26 20:23:25 +01:00
|
|
|
import envoy.util.EnvoyLog;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Project: <strong>envoy-client</strong><br>
|
|
|
|
* File: <strong>ChatSceneController.java</strong><br>
|
|
|
|
* Created: <strong>26.03.2020</strong><br>
|
2020-04-02 22:03:43 +02:00
|
|
|
*
|
2020-03-26 20:23:25 +01:00
|
|
|
* @author Kai S. K. Engelbart
|
|
|
|
* @since Envoy Client v0.1-beta
|
|
|
|
*/
|
2020-07-04 15:26:12 +02:00
|
|
|
public final class ChatScene implements Restorable {
|
2020-03-26 20:23:25 +01:00
|
|
|
|
2020-03-28 10:39:15 +01:00
|
|
|
@FXML
|
2020-03-28 15:32:24 +01:00
|
|
|
private Label contactLabel;
|
2020-03-28 10:39:15 +01:00
|
|
|
|
|
|
|
@FXML
|
2020-03-28 15:32:24 +01:00
|
|
|
private ListView<Message> messageList;
|
|
|
|
|
|
|
|
@FXML
|
2020-07-10 22:41:59 +02:00
|
|
|
private ListView<Chat> userList;
|
2020-03-28 15:32:24 +01:00
|
|
|
|
|
|
|
@FXML
|
|
|
|
private Button postButton;
|
|
|
|
|
2020-07-03 23:32:22 +02:00
|
|
|
@FXML
|
|
|
|
private Button voiceButton;
|
|
|
|
|
2020-07-09 16:05:09 +02:00
|
|
|
@FXML
|
|
|
|
private Button attachmentButton;
|
|
|
|
|
2020-03-28 15:32:24 +01:00
|
|
|
@FXML
|
|
|
|
private Button settingsButton;
|
|
|
|
|
|
|
|
@FXML
|
|
|
|
private TextArea messageTextArea;
|
|
|
|
|
2020-06-07 23:28:25 +02:00
|
|
|
@FXML
|
|
|
|
private Label remainingChars;
|
|
|
|
|
2020-06-26 23:16:03 +02:00
|
|
|
@FXML
|
|
|
|
private Label infoLabel;
|
|
|
|
|
2020-06-23 18:32:34 +02:00
|
|
|
@FXML
|
|
|
|
private MenuItem deleteContactMenuItem;
|
|
|
|
|
2020-07-05 23:25:07 +02:00
|
|
|
@FXML
|
|
|
|
private ImageView attachmentView;
|
|
|
|
|
2020-06-06 18:30:09 +02:00
|
|
|
private LocalDB localDB;
|
|
|
|
private Client client;
|
|
|
|
private WriteProxy writeProxy;
|
|
|
|
private SceneContext sceneContext;
|
2020-03-28 15:32:24 +01:00
|
|
|
|
2020-07-03 23:32:22 +02:00
|
|
|
private Chat currentChat;
|
|
|
|
private AudioRecorder recorder;
|
|
|
|
private boolean recording;
|
|
|
|
private Attachment pendingAttachment;
|
2020-07-09 22:47:29 +02:00
|
|
|
private boolean postingPermanentlyDisabled;
|
2020-03-28 10:39:15 +01:00
|
|
|
|
2020-07-10 20:53:28 +02:00
|
|
|
private static final Settings settings = Settings.getInstance();
|
|
|
|
private static final EventBus eventBus = EventBus.getInstance();
|
|
|
|
private static final Logger logger = EnvoyLog.getLogger(ChatScene.class);
|
2020-03-28 10:39:15 +01:00
|
|
|
|
2020-07-10 20:53:28 +02:00
|
|
|
private static final Image DEFAULT_ATTACHMENT_VIEW_IMAGE = IconUtil.loadIconThemeSensitive("attachment_present", 20);
|
|
|
|
private static final int MAX_MESSAGE_LENGTH = 255;
|
|
|
|
private static final int DEFAULT_ICON_SIZE = 16;
|
2020-03-26 20:23:25 +01:00
|
|
|
|
2020-06-09 17:11:17 +02:00
|
|
|
/**
|
|
|
|
* Initializes the appearance of certain visual components.
|
|
|
|
*
|
|
|
|
* @since Envoy Client v0.1-beta
|
|
|
|
*/
|
2020-03-28 10:39:15 +01:00
|
|
|
@FXML
|
2020-03-28 15:32:24 +01:00
|
|
|
private void initialize() {
|
2020-03-29 22:15:05 +02:00
|
|
|
|
|
|
|
// Initialize message and user rendering
|
2020-07-01 19:35:15 +02:00
|
|
|
messageList.setCellFactory(MessageListCellFactory::new);
|
|
|
|
userList.setCellFactory(ContactListCellFactory::new);
|
2020-03-29 22:15:05 +02:00
|
|
|
|
2020-07-09 22:47:29 +02:00
|
|
|
settingsButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("settings", DEFAULT_ICON_SIZE)));
|
|
|
|
voiceButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("microphone", DEFAULT_ICON_SIZE)));
|
|
|
|
attachmentButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("attachment", DEFAULT_ICON_SIZE)));
|
2020-07-10 20:53:28 +02:00
|
|
|
attachmentView.setImage(DEFAULT_ATTACHMENT_VIEW_IMAGE);
|
2020-06-26 17:33:32 +02:00
|
|
|
|
2020-03-29 22:15:05 +02:00
|
|
|
// Listen to received messages
|
|
|
|
eventBus.register(MessageCreationEvent.class, e -> {
|
2020-06-10 13:05:44 +02:00
|
|
|
final var message = e.get();
|
2020-07-05 13:28:17 +02:00
|
|
|
localDB.getChat(message instanceof GroupMessage ? message.getRecipientID() : message.getSenderID()).ifPresent(chat -> {
|
2020-07-01 08:36:21 +02:00
|
|
|
chat.insert(message);
|
2020-06-12 10:48:33 +02:00
|
|
|
if (chat.equals(currentChat)) {
|
|
|
|
try {
|
|
|
|
currentChat.read(writeProxy);
|
2020-06-18 22:20:34 +02:00
|
|
|
} catch (final IOException e1) {
|
2020-06-13 17:00:44 +02:00
|
|
|
logger.log(Level.WARNING, "Could not read current chat: ", e1);
|
2020-06-12 10:48:33 +02:00
|
|
|
}
|
2020-06-22 20:53:44 +02:00
|
|
|
Platform.runLater(() -> { messageList.refresh(); scrollToMessageListEnd(); });
|
2020-07-12 14:34:07 +02:00
|
|
|
} else chat.incrementUnreadAmount();
|
2020-07-11 14:18:38 +02:00
|
|
|
// Moving chat with most recent unreadMessages to the top
|
|
|
|
Platform.runLater(() -> {
|
|
|
|
userList.getItems().remove(chat);
|
|
|
|
userList.getItems().add(0, chat);
|
2020-07-11 14:46:13 +02:00
|
|
|
if (chat.equals(currentChat)) userList.getSelectionModel().select(0);
|
2020-07-11 14:18:38 +02:00
|
|
|
localDB.getChats().remove(chat);
|
|
|
|
localDB.getChats().add(0, chat);
|
|
|
|
});
|
2020-06-10 13:05:44 +02:00
|
|
|
});
|
2020-03-29 22:15:05 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
// Listen to message status changes
|
2020-06-20 10:00:38 +02:00
|
|
|
eventBus.register(MessageStatusChange.class, e -> localDB.getMessage(e.getID()).ifPresent(message -> {
|
2020-03-29 22:15:05 +02:00
|
|
|
message.setStatus(e.get());
|
|
|
|
// Update UI if in current chat
|
2020-06-09 15:41:01 +02:00
|
|
|
if (currentChat != null && message.getSenderID() == currentChat.getRecipient().getID()) Platform.runLater(messageList::refresh);
|
2020-06-10 13:05:44 +02:00
|
|
|
}));
|
2020-06-09 15:41:01 +02:00
|
|
|
|
2020-07-04 15:43:46 +02:00
|
|
|
eventBus.register(GroupMessageStatusChange.class, e -> localDB.getMessage(e.getID()).ifPresent(groupMessage -> {
|
|
|
|
((GroupMessage) groupMessage).getMemberStatuses().replace(e.getMemberID(), e.get());
|
|
|
|
|
2020-07-08 14:15:44 +02:00
|
|
|
// Update UI if in current chat
|
2020-07-04 15:43:46 +02:00
|
|
|
if (currentChat != null && groupMessage.getRecipientID() == currentChat.getRecipient().getID()) Platform.runLater(messageList::refresh);
|
|
|
|
}));
|
2020-06-09 15:41:01 +02:00
|
|
|
|
2020-03-29 22:15:05 +02:00
|
|
|
// Listen to user status changes
|
2020-06-20 10:00:38 +02:00
|
|
|
eventBus.register(UserStatusChange.class,
|
2020-06-13 22:36:52 +02:00
|
|
|
e -> userList.getItems()
|
|
|
|
.stream()
|
2020-07-10 22:41:59 +02:00
|
|
|
.filter(c -> c.getRecipient().getID() == e.getID())
|
2020-06-13 22:36:52 +02:00
|
|
|
.findAny()
|
2020-07-10 22:41:59 +02:00
|
|
|
.map(u -> u.getRecipient())
|
2020-06-13 22:36:52 +02:00
|
|
|
.ifPresent(u -> { ((User) u).setStatus(e.get()); Platform.runLater(userList::refresh); }));
|
2020-06-07 20:46:04 +02:00
|
|
|
|
|
|
|
// Listen to contacts changes
|
2020-06-20 10:00:38 +02:00
|
|
|
eventBus.register(ContactOperation.class, e -> {
|
2020-06-07 20:46:04 +02:00
|
|
|
final var contact = e.get();
|
2020-06-09 15:41:01 +02:00
|
|
|
switch (e.getOperationType()) {
|
|
|
|
case ADD:
|
|
|
|
localDB.getUsers().put(contact.getName(), contact);
|
2020-07-10 22:41:59 +02:00
|
|
|
Chat chat = contact instanceof User ? new Chat(contact) : new GroupChat(client.getSender(), contact);
|
|
|
|
localDB.getChats().add(chat);
|
|
|
|
Platform.runLater(() -> userList.getItems().add(chat));
|
2020-06-09 15:41:01 +02:00
|
|
|
break;
|
|
|
|
case REMOVE:
|
|
|
|
localDB.getUsers().remove(contact.getName());
|
|
|
|
localDB.getChats().removeIf(c -> c.getRecipient().getID() == contact.getID());
|
2020-07-10 22:41:59 +02:00
|
|
|
Platform.runLater(() -> userList.getItems().removeIf(c -> c.getRecipient().getID() == contact.getID()));
|
2020-06-09 15:41:01 +02:00
|
|
|
break;
|
|
|
|
}
|
2020-06-07 20:46:04 +02:00
|
|
|
});
|
2020-03-28 10:39:15 +01:00
|
|
|
}
|
|
|
|
|
2020-06-08 11:58:57 +02:00
|
|
|
/**
|
2020-06-09 17:11:17 +02:00
|
|
|
* Initializes all necessary data via dependency injection-
|
|
|
|
*
|
2020-06-08 11:58:57 +02:00
|
|
|
* @param sceneContext the scene context used to load other scenes
|
|
|
|
* @param localDB the local database form which chats and users are loaded
|
|
|
|
* @param client the client used to request ID generators
|
|
|
|
* @param writeProxy the write proxy used to send messages and other data to
|
|
|
|
* the server
|
|
|
|
* @since Envoy Client v0.1-beta
|
|
|
|
*/
|
2020-06-08 10:36:30 +02:00
|
|
|
public void initializeData(SceneContext sceneContext, LocalDB localDB, Client client, WriteProxy writeProxy) {
|
2020-06-06 18:30:09 +02:00
|
|
|
this.sceneContext = sceneContext;
|
|
|
|
this.localDB = localDB;
|
|
|
|
this.client = client;
|
|
|
|
this.writeProxy = writeProxy;
|
2020-03-28 15:32:24 +01:00
|
|
|
|
2020-07-10 22:41:59 +02:00
|
|
|
userList.setItems(FXCollections.observableList(localDB.getChats().stream().collect(Collectors.toList())));
|
2020-06-22 20:53:44 +02:00
|
|
|
contactLabel.setText(localDB.getUser().getName());
|
2020-07-01 19:35:15 +02:00
|
|
|
MessageControl.setUser(localDB.getUser());
|
|
|
|
if (!client.isOnline()) updateInfoLabel("You are offline", "infoLabel-info");
|
2020-07-03 23:32:22 +02:00
|
|
|
|
|
|
|
recorder = new AudioRecorder();
|
2020-03-28 15:32:24 +01:00
|
|
|
}
|
|
|
|
|
2020-07-04 15:26:12 +02:00
|
|
|
@Override
|
|
|
|
public void onRestore() { updateRemainingCharsLabel(); }
|
|
|
|
|
2020-06-09 17:11:17 +02:00
|
|
|
/**
|
|
|
|
* Actions to perform when the list of contacts has been clicked.
|
|
|
|
*
|
|
|
|
* @since Envoy Client v0.1-beta
|
|
|
|
*/
|
2020-03-26 20:23:25 +01:00
|
|
|
@FXML
|
2020-03-28 15:32:24 +01:00
|
|
|
private void userListClicked() {
|
2020-07-10 22:41:59 +02:00
|
|
|
final Contact user = userList.getSelectionModel().getSelectedItem().getRecipient();
|
2020-06-12 10:48:33 +02:00
|
|
|
if (user != null && (currentChat == null || !user.equals(currentChat.getRecipient()))) {
|
2020-03-28 15:32:24 +01:00
|
|
|
|
|
|
|
// LEON: JFC <===> JAVA FRIED CHICKEN <=/=> Java Foundation Classes
|
|
|
|
|
2020-06-26 09:08:41 +02:00
|
|
|
// Load the chat
|
|
|
|
currentChat = localDB.getChat(user.getID()).get();
|
2020-03-30 21:27:07 +02:00
|
|
|
|
|
|
|
messageList.setItems(FXCollections.observableList(currentChat.getMessages()));
|
2020-07-11 14:57:15 +02:00
|
|
|
final var scrollIndex = messageList.getItems().size() - currentChat.getUnreadAmount() - 1;
|
2020-07-01 19:35:15 +02:00
|
|
|
messageList.scrollTo(scrollIndex);
|
|
|
|
logger.log(Level.FINEST, "Loading chat with " + user + " at index " + scrollIndex);
|
2020-06-23 18:32:34 +02:00
|
|
|
deleteContactMenuItem.setText("Delete " + user.getName());
|
2020-06-07 23:28:25 +02:00
|
|
|
|
2020-06-12 10:48:33 +02:00
|
|
|
// Read the current chat
|
|
|
|
try {
|
|
|
|
currentChat.read(writeProxy);
|
2020-06-18 22:20:34 +02:00
|
|
|
} catch (final IOException e) {
|
2020-06-12 10:48:33 +02:00
|
|
|
logger.log(Level.WARNING, "Could not read current chat.", e);
|
|
|
|
}
|
2020-06-07 23:28:25 +02:00
|
|
|
|
2020-07-03 23:32:22 +02:00
|
|
|
// Discard the pending attachment
|
2020-07-05 12:21:07 +02:00
|
|
|
if (recorder.isRecording()) {
|
|
|
|
recorder.cancel();
|
|
|
|
recording = false;
|
|
|
|
}
|
2020-07-03 23:32:22 +02:00
|
|
|
pendingAttachment = null;
|
2020-07-10 20:53:28 +02:00
|
|
|
updateAttachmentView(false);
|
2020-07-03 23:32:22 +02:00
|
|
|
|
2020-06-07 23:28:25 +02:00
|
|
|
remainingChars.setVisible(true);
|
|
|
|
remainingChars
|
|
|
|
.setText(String.format("remaining chars: %d/%d", MAX_MESSAGE_LENGTH - messageTextArea.getText().length(), MAX_MESSAGE_LENGTH));
|
2020-03-28 15:32:24 +01:00
|
|
|
}
|
2020-06-27 09:34:30 +02:00
|
|
|
messageTextArea.setDisable(currentChat == null || postingPermanentlyDisabled);
|
2020-07-05 12:21:07 +02:00
|
|
|
voiceButton.setDisable(!recorder.isSupported());
|
2020-07-09 16:05:09 +02:00
|
|
|
attachmentButton.setDisable(false);
|
2020-07-10 23:25:55 +02:00
|
|
|
userList.refresh();
|
2020-03-26 20:23:25 +01:00
|
|
|
}
|
|
|
|
|
2020-06-09 17:11:17 +02:00
|
|
|
/**
|
|
|
|
* Actions to perform when the Settings Button has been clicked.
|
|
|
|
*
|
|
|
|
* @since Envoy Client v0.1-beta
|
|
|
|
*/
|
2020-03-28 15:32:24 +01:00
|
|
|
@FXML
|
2020-04-18 19:46:04 +02:00
|
|
|
private void settingsButtonClicked() {
|
2020-06-08 11:58:57 +02:00
|
|
|
sceneContext.load(SceneContext.SceneInfo.SETTINGS_SCENE);
|
|
|
|
sceneContext.<SettingsScene>getController().initializeData(sceneContext);
|
2020-06-07 20:46:04 +02:00
|
|
|
}
|
|
|
|
|
2020-06-09 17:11:17 +02:00
|
|
|
/**
|
|
|
|
* Actions to perform when the "Add Contact" - Button has been clicked.
|
|
|
|
*
|
|
|
|
* @since Envoy Client v0.1-beta
|
|
|
|
*/
|
2020-06-07 20:46:04 +02:00
|
|
|
@FXML
|
|
|
|
private void addContactButtonClicked() {
|
2020-06-08 11:58:57 +02:00
|
|
|
sceneContext.load(SceneContext.SceneInfo.CONTACT_SEARCH_SCENE);
|
2020-06-09 21:22:45 +02:00
|
|
|
sceneContext.<ContactSearchScene>getController().initializeData(sceneContext, localDB);
|
2020-04-18 19:46:04 +02:00
|
|
|
}
|
2020-03-28 15:32:24 +01:00
|
|
|
|
2020-07-03 23:32:22 +02:00
|
|
|
@FXML
|
|
|
|
private void voiceButtonClicked() {
|
|
|
|
new Thread(() -> {
|
|
|
|
try {
|
|
|
|
if (!recording) {
|
|
|
|
recording = true;
|
2020-07-06 22:35:06 +02:00
|
|
|
Platform.runLater(() -> {
|
|
|
|
voiceButton.setText("Recording");
|
2020-07-09 22:47:29 +02:00
|
|
|
voiceButton.setGraphic(new ImageView(IconUtil.loadIcon("microphone_recording", DEFAULT_ICON_SIZE)));
|
2020-07-06 22:35:06 +02:00
|
|
|
});
|
2020-07-03 23:32:22 +02:00
|
|
|
recorder.start();
|
|
|
|
} else {
|
|
|
|
pendingAttachment = new Attachment(recorder.finish(), AttachmentType.VOICE);
|
|
|
|
recording = false;
|
2020-07-05 23:25:07 +02:00
|
|
|
Platform.runLater(() -> {
|
2020-07-09 22:47:29 +02:00
|
|
|
voiceButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("microphone", DEFAULT_ICON_SIZE)));
|
2020-07-06 22:35:06 +02:00
|
|
|
voiceButton.setText(null);
|
2020-07-05 23:25:07 +02:00
|
|
|
checkPostConditions(false);
|
2020-07-10 20:53:28 +02:00
|
|
|
updateAttachmentView(true);
|
2020-07-05 23:25:07 +02:00
|
|
|
});
|
2020-07-03 23:32:22 +02:00
|
|
|
}
|
2020-07-05 23:25:07 +02:00
|
|
|
} catch (final EnvoyException e) {
|
2020-07-05 14:25:58 +02:00
|
|
|
logger.log(Level.SEVERE, "Could not record audio: ", e);
|
|
|
|
Platform.runLater(new Alert(AlertType.ERROR, "Could not record audio")::showAndWait);
|
2020-07-03 23:32:22 +02:00
|
|
|
}
|
|
|
|
}).start();
|
|
|
|
}
|
|
|
|
|
2020-07-09 16:05:09 +02:00
|
|
|
@FXML
|
|
|
|
private void attachmentButtonClicked() {
|
|
|
|
|
|
|
|
// Display file chooser
|
|
|
|
final var fileChooser = new FileChooser();
|
|
|
|
fileChooser.setTitle("Add Attachment");
|
|
|
|
fileChooser.setInitialDirectory(new File(System.getProperty("user.home")));
|
|
|
|
fileChooser.getExtensionFilters()
|
2020-07-09 22:47:29 +02:00
|
|
|
.addAll(new FileChooser.ExtensionFilter("Pictures", "*.png", "*.jpg", "*.bmp", "*.gif"),
|
2020-07-09 16:05:09 +02:00
|
|
|
new FileChooser.ExtensionFilter("Videos", "*.mp4"),
|
|
|
|
new FileChooser.ExtensionFilter("All Files", "*.*"));
|
|
|
|
final var file = fileChooser.showOpenDialog(sceneContext.getStage());
|
|
|
|
|
2020-07-09 22:47:29 +02:00
|
|
|
if (file != null) {
|
2020-07-09 16:05:09 +02:00
|
|
|
|
|
|
|
// Check max file size
|
|
|
|
if (file.length() > 16E6) {
|
|
|
|
new Alert(AlertType.WARNING, "The selected file exceeds the size limit of 16MB!").showAndWait();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get attachment type (default is document)
|
|
|
|
AttachmentType type = AttachmentType.DOCUMENT;
|
|
|
|
switch (fileChooser.getSelectedExtensionFilter().getDescription()) {
|
|
|
|
case "Pictures":
|
|
|
|
type = AttachmentType.PICTURE;
|
|
|
|
break;
|
|
|
|
case "Videos":
|
|
|
|
type = AttachmentType.VIDEO;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the pending attachment
|
|
|
|
try {
|
2020-07-10 20:53:28 +02:00
|
|
|
final var fileBytes = Files.readAllBytes(file.toPath());
|
|
|
|
pendingAttachment = new Attachment(fileBytes, type);
|
|
|
|
// Setting the preview image as image of the attachmentView
|
|
|
|
if (type == AttachmentType.PICTURE)
|
|
|
|
attachmentView.setImage(new Image(new ByteArrayInputStream(fileBytes), DEFAULT_ICON_SIZE, DEFAULT_ICON_SIZE, true, true));
|
2020-07-09 16:05:09 +02:00
|
|
|
attachmentView.setVisible(true);
|
2020-07-09 22:47:29 +02:00
|
|
|
} catch (final IOException e) {
|
2020-07-09 16:05:09 +02:00
|
|
|
new Alert(AlertType.ERROR, "The selected file could not be loaded!").showAndWait();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-13 22:38:49 +02:00
|
|
|
/**
|
|
|
|
* Checks the text length of the {@code messageTextArea}, adjusts the
|
2020-06-14 16:03:02 +02:00
|
|
|
* {@code remainingChars} label and checks whether to send the message
|
2020-06-13 22:38:49 +02:00
|
|
|
* automatically.
|
|
|
|
*
|
2020-06-14 16:03:02 +02:00
|
|
|
* @param e the key event that will be analyzed for a post request
|
2020-06-13 22:38:49 +02:00
|
|
|
* @since Envoy Client v0.1-beta
|
|
|
|
*/
|
|
|
|
@FXML
|
|
|
|
private void checkKeyCombination(KeyEvent e) {
|
2020-06-14 16:03:02 +02:00
|
|
|
// Checks whether the text is too long
|
2020-06-13 22:38:49 +02:00
|
|
|
messageTextUpdated();
|
|
|
|
// Automatic sending of messages via (ctrl +) enter
|
|
|
|
checkPostConditions(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param e the keys that have been pressed
|
|
|
|
* @since Envoy Client v0.1-beta
|
|
|
|
*/
|
|
|
|
@FXML
|
|
|
|
private void checkPostConditions(KeyEvent e) {
|
2020-07-03 23:32:22 +02:00
|
|
|
checkPostConditions(settings.isEnterToSend() && e.getCode() == KeyCode.ENTER
|
|
|
|
|| !settings.isEnterToSend() && e.getCode() == KeyCode.ENTER && e.isControlDown());
|
|
|
|
}
|
|
|
|
|
|
|
|
private void checkPostConditions(boolean sendKeyPressed) {
|
2020-06-26 23:16:03 +02:00
|
|
|
if (!postingPermanentlyDisabled) {
|
2020-07-05 12:21:07 +02:00
|
|
|
if (!postButton.isDisabled() && sendKeyPressed) postMessage();
|
2020-07-06 22:33:04 +02:00
|
|
|
postButton.setDisable(messageTextArea.getText().isBlank() && pendingAttachment == null || currentChat == null);
|
2020-06-27 09:34:30 +02:00
|
|
|
} else {
|
|
|
|
final var noMoreMessaging = "Go online to send messages";
|
|
|
|
if (!infoLabel.getText().equals(noMoreMessaging))
|
|
|
|
// Informing the user that he is a f*cking moron and should use Envoy online
|
|
|
|
// because he ran out of messageIDs to use
|
2020-07-01 19:35:15 +02:00
|
|
|
updateInfoLabel(noMoreMessaging, "infoLabel-error");
|
2020-06-27 09:34:30 +02:00
|
|
|
}
|
2020-06-13 22:38:49 +02:00
|
|
|
}
|
|
|
|
|
2020-06-09 17:11:17 +02:00
|
|
|
/**
|
|
|
|
* Actions to perform when the text was updated in the messageTextArea.
|
|
|
|
*
|
|
|
|
* @since Envoy Client v0.1-beta
|
|
|
|
*/
|
2020-03-28 15:32:24 +01:00
|
|
|
@FXML
|
2020-06-09 17:11:17 +02:00
|
|
|
private void messageTextUpdated() {
|
2020-06-07 23:28:25 +02:00
|
|
|
// Truncating messages that are too long and staying at the same position
|
|
|
|
if (messageTextArea.getText().length() >= MAX_MESSAGE_LENGTH) {
|
|
|
|
messageTextArea.setText(messageTextArea.getText().substring(0, MAX_MESSAGE_LENGTH));
|
|
|
|
messageTextArea.positionCaret(MAX_MESSAGE_LENGTH);
|
|
|
|
messageTextArea.setScrollTop(Double.MAX_VALUE);
|
|
|
|
}
|
2020-06-14 16:11:46 +02:00
|
|
|
updateRemainingCharsLabel();
|
2020-03-28 15:32:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-06-14 16:03:02 +02:00
|
|
|
* Sets the text and text color of the {@code remainingChars} label.
|
2020-03-28 15:32:24 +01:00
|
|
|
*
|
|
|
|
* @since Envoy Client v0.1-beta
|
|
|
|
*/
|
2020-06-14 16:11:46 +02:00
|
|
|
private void updateRemainingCharsLabel() {
|
2020-06-13 22:38:49 +02:00
|
|
|
final int currentLength = messageTextArea.getText().length();
|
|
|
|
final int remainingLength = MAX_MESSAGE_LENGTH - currentLength;
|
|
|
|
remainingChars.setText(String.format("remaining chars: %d/%d", remainingLength, MAX_MESSAGE_LENGTH));
|
|
|
|
remainingChars.setTextFill(Color.rgb(currentLength, remainingLength, 0, 1));
|
2020-03-28 15:32:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-07-08 14:15:44 +02:00
|
|
|
* Sends a new {@link Message} or {@link GroupMessage} to the server based on
|
|
|
|
* the text entered in the {@code messageTextArea} and the given attachment.
|
2020-03-28 15:32:24 +01:00
|
|
|
*
|
|
|
|
* @since Envoy Client v0.1-beta
|
|
|
|
*/
|
2020-06-10 13:05:44 +02:00
|
|
|
@FXML
|
2020-03-28 15:32:24 +01:00
|
|
|
private void postMessage() {
|
2020-06-26 23:16:03 +02:00
|
|
|
postingPermanentlyDisabled = !(client.isOnline() || localDB.getIDGenerator().hasNext());
|
|
|
|
if (postingPermanentlyDisabled) {
|
|
|
|
postButton.setDisable(true);
|
2020-06-27 09:34:30 +02:00
|
|
|
messageTextArea.setDisable(true);
|
|
|
|
messageTextArea.clear();
|
2020-07-01 19:35:15 +02:00
|
|
|
updateInfoLabel("You need to go online to send more messages", "infoLabel-error");
|
2020-06-26 23:16:03 +02:00
|
|
|
return;
|
|
|
|
}
|
2020-06-13 22:38:49 +02:00
|
|
|
final var text = messageTextArea.getText().strip();
|
2020-03-28 15:32:24 +01:00
|
|
|
try {
|
2020-07-05 17:01:11 +02:00
|
|
|
// Creating the message and its metadata
|
2020-07-09 22:47:29 +02:00
|
|
|
final var builder = new MessageBuilder(localDB.getUser().getID(), currentChat.getRecipient().getID(), localDB.getIDGenerator())
|
2020-07-05 11:15:00 +02:00
|
|
|
.setText(text);
|
2020-07-09 16:05:09 +02:00
|
|
|
// Setting an attachment, if present
|
|
|
|
if (pendingAttachment != null) {
|
2020-07-03 23:32:22 +02:00
|
|
|
builder.setAttachment(pendingAttachment);
|
|
|
|
pendingAttachment = null;
|
2020-07-10 20:53:28 +02:00
|
|
|
updateAttachmentView(false);
|
2020-07-03 23:32:22 +02:00
|
|
|
}
|
2020-07-09 16:05:09 +02:00
|
|
|
// Building the final message
|
2020-07-09 22:47:29 +02:00
|
|
|
final var message = currentChat.getRecipient() instanceof Group ? builder.buildGroupMessage((Group) currentChat.getRecipient())
|
2020-07-05 11:15:00 +02:00
|
|
|
: builder.build();
|
2020-03-28 15:32:24 +01:00
|
|
|
|
|
|
|
// Send message
|
|
|
|
writeProxy.writeMessage(message);
|
|
|
|
|
|
|
|
// Add message to LocalDB and update UI
|
2020-07-01 08:45:39 +02:00
|
|
|
currentChat.insert(message);
|
2020-07-11 14:18:38 +02:00
|
|
|
// Moving currentChat to the top
|
|
|
|
Platform.runLater(() -> {
|
|
|
|
userList.getItems().remove(currentChat);
|
|
|
|
userList.getItems().add(0, currentChat);
|
|
|
|
userList.getSelectionModel().select(0);
|
|
|
|
localDB.getChats().remove(currentChat);
|
|
|
|
localDB.getChats().add(0, currentChat);
|
|
|
|
});
|
2020-07-01 08:45:39 +02:00
|
|
|
messageList.refresh();
|
2020-06-22 20:53:44 +02:00
|
|
|
scrollToMessageListEnd();
|
2020-03-28 15:32:24 +01:00
|
|
|
|
|
|
|
// Request a new ID generator if all IDs were used
|
2020-03-29 09:16:29 +02:00
|
|
|
if (!localDB.getIDGenerator().hasNext() && client.isOnline()) client.requestIdGenerator();
|
2020-03-28 15:32:24 +01:00
|
|
|
|
2020-06-07 20:46:04 +02:00
|
|
|
} catch (final IOException e) {
|
2020-06-20 22:29:32 +02:00
|
|
|
logger.log(Level.SEVERE, "Error while sending message: ", e);
|
2020-06-10 13:05:44 +02:00
|
|
|
new Alert(AlertType.ERROR, "An error occured while sending the message!").showAndWait();
|
2020-03-28 15:32:24 +01:00
|
|
|
}
|
2020-06-10 13:05:44 +02:00
|
|
|
|
|
|
|
// Clear text field and disable post button
|
|
|
|
messageTextArea.setText("");
|
|
|
|
postButton.setDisable(true);
|
2020-06-14 16:11:46 +02:00
|
|
|
updateRemainingCharsLabel();
|
2020-03-26 20:23:25 +01:00
|
|
|
}
|
2020-06-22 20:53:44 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Scrolls to the bottom of the {@code messageList}.
|
|
|
|
*
|
|
|
|
* @since Envoy Client v0.1-beta
|
|
|
|
*/
|
|
|
|
private void scrollToMessageListEnd() { messageList.scrollTo(messageList.getItems().size() - 1); }
|
2020-06-22 22:51:10 +02:00
|
|
|
|
2020-06-26 23:16:03 +02:00
|
|
|
/**
|
|
|
|
* Updates the {@code infoLabel}.
|
|
|
|
*
|
2020-07-01 19:35:15 +02:00
|
|
|
* @param text the text to use
|
|
|
|
* @param infoLabelID the id the the {@code infoLabel} should have so that it
|
|
|
|
* can be styled accordingly in CSS
|
2020-06-26 23:16:03 +02:00
|
|
|
* @since Envoy Client v0.1-beta
|
|
|
|
*/
|
2020-07-01 19:35:15 +02:00
|
|
|
private void updateInfoLabel(String text, String infoLabelID) {
|
2020-06-26 23:16:03 +02:00
|
|
|
infoLabel.setText(text);
|
2020-07-01 19:35:15 +02:00
|
|
|
infoLabel.setId(infoLabelID);
|
2020-06-26 23:16:03 +02:00
|
|
|
infoLabel.setVisible(true);
|
|
|
|
}
|
|
|
|
|
2020-07-10 20:53:28 +02:00
|
|
|
/**
|
2020-07-10 23:05:57 +02:00
|
|
|
* Updates the {@code attachmentView} in terms of visibility.<br>
|
2020-07-10 20:53:28 +02:00
|
|
|
* Additionally resets the shown image to
|
|
|
|
* {@code DEFAULT_ATTACHMENT_VIEW_IMAGE} if another image is currently
|
|
|
|
* present.
|
|
|
|
*
|
|
|
|
* @param visible whether the {@code attachmentView} should be displayed
|
|
|
|
* @since Envoy Client v0.1-beta
|
|
|
|
*/
|
|
|
|
private void updateAttachmentView(boolean visible) {
|
|
|
|
if (!attachmentView.getImage().equals(DEFAULT_ATTACHMENT_VIEW_IMAGE)) attachmentView.setImage(DEFAULT_ATTACHMENT_VIEW_IMAGE);
|
|
|
|
attachmentView.setVisible(visible);
|
|
|
|
}
|
|
|
|
|
2020-06-25 11:19:59 +02:00
|
|
|
// Context menu actions
|
2020-07-09 16:05:09 +02:00
|
|
|
|
2020-06-23 18:32:34 +02:00
|
|
|
@FXML
|
2020-06-23 23:32:31 +02:00
|
|
|
private void deleteContact() { try {} catch (final NullPointerException e) {} }
|
2020-06-23 18:32:34 +02:00
|
|
|
|
|
|
|
@FXML
|
|
|
|
private void copyAndPostMessage() {
|
2020-06-25 11:17:25 +02:00
|
|
|
final var messageText = messageTextArea.getText();
|
|
|
|
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(messageText), null);
|
|
|
|
postMessage();
|
|
|
|
messageTextArea.setText(messageText);
|
|
|
|
updateRemainingCharsLabel();
|
|
|
|
postButton.setDisable(messageText.isBlank());
|
2020-03-26 20:23:25 +01:00
|
|
|
}
|
|
|
|
}
|