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-09-02 11:21:36 +02:00
|
|
|
import java.io.*;
|
2020-07-09 16:05:09 +02:00
|
|
|
import java.nio.file.Files;
|
2020-07-27 12:00:49 +02:00
|
|
|
import java.time.LocalDateTime;
|
|
|
|
import java.time.format.DateTimeFormatter;
|
2020-07-11 15:23:57 +02:00
|
|
|
import java.util.Random;
|
2020-09-02 11:21:36 +02:00
|
|
|
import java.util.logging.*;
|
2020-03-26 20:23:25 +01:00
|
|
|
|
2020-07-11 15:23:57 +02:00
|
|
|
import javafx.animation.RotateTransition;
|
2020-04-18 19:46:04 +02:00
|
|
|
import javafx.application.Platform;
|
2020-09-25 19:19:54 +02:00
|
|
|
import javafx.collections.ObservableList;
|
2020-07-31 18:46:32 +02:00
|
|
|
import javafx.collections.transformation.FilteredList;
|
2020-09-02 11:21:36 +02:00
|
|
|
import javafx.fxml.*;
|
2020-04-18 19:46:04 +02:00
|
|
|
import javafx.scene.control.*;
|
2020-06-10 13:05:44 +02:00
|
|
|
import javafx.scene.control.Alert.AlertType;
|
2020-09-02 11:21:36 +02:00
|
|
|
import javafx.scene.image.*;
|
|
|
|
import javafx.scene.input.*;
|
|
|
|
import javafx.scene.layout.*;
|
2020-06-07 23:28:25 +02:00
|
|
|
import javafx.scene.paint.Color;
|
2020-07-17 13:56:36 +02:00
|
|
|
import javafx.scene.shape.Rectangle;
|
2020-07-09 16:05:09 +02:00
|
|
|
import javafx.stage.FileChooser;
|
2020-07-11 15:23:57 +02:00
|
|
|
import javafx.util.Duration;
|
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-09-02 11:21:36 +02:00
|
|
|
import envoy.client.data.commands.*;
|
2020-08-23 20:26:22 +02:00
|
|
|
import envoy.client.event.*;
|
2020-09-23 23:11:32 +02:00
|
|
|
import envoy.client.helper.ShutdownHelper;
|
2020-09-02 11:21:36 +02:00
|
|
|
import envoy.client.net.*;
|
2020-07-16 17:35:15 +02:00
|
|
|
import envoy.client.ui.*;
|
2020-09-23 23:11:32 +02:00
|
|
|
import envoy.client.ui.SceneContext.SceneInfo;
|
2020-09-25 14:29:23 +02:00
|
|
|
import envoy.client.ui.control.*;
|
2020-07-18 11:50:49 +02:00
|
|
|
import envoy.client.ui.listcell.*;
|
2020-09-25 14:29:23 +02:00
|
|
|
import envoy.client.util.*;
|
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-09-08 20:41:01 +02:00
|
|
|
import envoy.data.Message.MessageStatus;
|
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;
|
|
|
|
|
2020-09-08 20:41:01 +02:00
|
|
|
import dev.kske.eventbus.*;
|
|
|
|
import dev.kske.eventbus.Event;
|
|
|
|
|
2020-03-26 20:23:25 +01:00
|
|
|
/**
|
2020-09-25 14:29:23 +02:00
|
|
|
* Controller for the chat scene.
|
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-09-08 20:41:01 +02:00
|
|
|
public final class ChatScene implements EventListener, Restorable {
|
2020-03-26 20:23:25 +01:00
|
|
|
|
2020-07-11 15:23:57 +02:00
|
|
|
@FXML
|
|
|
|
private GridPane scene;
|
|
|
|
|
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-12 17:10:22 +02:00
|
|
|
private ListView<Chat> chatList;
|
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;
|
|
|
|
|
2020-08-22 18:14:26 +02:00
|
|
|
@FXML
|
|
|
|
private Button messageSearchButton;
|
|
|
|
|
|
|
|
@FXML
|
|
|
|
private Button newGroupButton;
|
|
|
|
|
2020-09-01 20:57:18 +02:00
|
|
|
@FXML
|
|
|
|
private Button newContactButton;
|
|
|
|
|
2020-03-28 15:32:24 +01:00
|
|
|
@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-07-16 21:13:46 +02:00
|
|
|
@FXML
|
|
|
|
private Label topBarContactLabel;
|
|
|
|
|
|
|
|
@FXML
|
|
|
|
private Label topBarStatusLabel;
|
|
|
|
|
2020-07-17 13:56:36 +02:00
|
|
|
@FXML
|
|
|
|
private ImageView clientProfilePic;
|
|
|
|
|
|
|
|
@FXML
|
|
|
|
private ImageView recipientProfilePic;
|
|
|
|
|
2020-07-31 18:46:32 +02:00
|
|
|
@FXML
|
|
|
|
private TextArea contactSearch;
|
2020-09-01 20:14:02 +02:00
|
|
|
|
2020-08-20 11:02:51 +02:00
|
|
|
@FXML
|
|
|
|
private VBox contactOperations;
|
2020-09-01 20:14:02 +02:00
|
|
|
|
2020-08-22 21:02:49 +02:00
|
|
|
@FXML
|
|
|
|
private TabPane tabPane;
|
2020-09-01 20:14:02 +02:00
|
|
|
|
2020-08-22 21:50:05 +02:00
|
|
|
@FXML
|
|
|
|
private Tab contactSearchTab;
|
2020-09-01 20:14:02 +02:00
|
|
|
|
2020-08-23 20:15:52 +02:00
|
|
|
@FXML
|
|
|
|
private Tab groupCreationTab;
|
2020-07-31 18:46:32 +02:00
|
|
|
|
2020-09-01 20:57:18 +02:00
|
|
|
@FXML
|
|
|
|
private HBox contactSpecificOnlineOperations;
|
2020-03-28 10:39:15 +01:00
|
|
|
|
2020-09-02 11:21:36 +02:00
|
|
|
private Chat currentChat;
|
|
|
|
private FilteredList<Chat> chats;
|
|
|
|
private boolean recording;
|
|
|
|
private Attachment pendingAttachment;
|
|
|
|
private boolean postingPermanentlyDisabled;
|
2020-09-02 11:31:21 +02:00
|
|
|
private boolean isCustomAttachmentImage;
|
2020-07-23 17:18:53 +02:00
|
|
|
|
2020-09-01 20:14:02 +02:00
|
|
|
private final LocalDB localDB = context.getLocalDB();
|
|
|
|
private final Client client = context.getClient();
|
|
|
|
private final WriteProxy writeProxy = context.getWriteProxy();
|
|
|
|
private final SceneContext sceneContext = context.getSceneContext();
|
|
|
|
private final AudioRecorder recorder = new AudioRecorder();
|
2020-09-23 17:03:32 +02:00
|
|
|
private final SystemCommandMap messageTextAreaCommands = new SystemCommandMap();
|
2020-09-01 20:57:18 +02:00
|
|
|
private final Tooltip onlyIfOnlineTooltip = new Tooltip("You need to be online to do this");
|
2020-07-10 20:53:28 +02:00
|
|
|
|
2020-09-02 11:21:36 +02:00
|
|
|
private static Image DEFAULT_ATTACHMENT_VIEW_IMAGE = IconUtil.loadIconThemeSensitive("attachment_present", 20);
|
2020-03-26 20:23:25 +01:00
|
|
|
|
2020-09-02 11:21:36 +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);
|
|
|
|
private static final Context context = Context.getInstance();
|
|
|
|
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-09-08 20:41:01 +02:00
|
|
|
eventBus.registerListener(this);
|
|
|
|
|
2020-03-29 22:15:05 +02:00
|
|
|
// Initialize message and user rendering
|
2020-07-18 11:50:49 +02:00
|
|
|
messageList.setCellFactory(MessageListCell::new);
|
2020-07-13 22:08:08 +02:00
|
|
|
chatList.setCellFactory(new ListCellFactory<>(ChatControl::new));
|
2020-03-29 22:15:05 +02:00
|
|
|
|
2020-09-25 19:19:54 +02:00
|
|
|
// JavaFX provides an internal way of populating the context menu of a text
|
|
|
|
// area.
|
2020-09-20 15:27:23 +02:00
|
|
|
// We, however, need additional functionality.
|
|
|
|
messageTextArea.setContextMenu(new TextInputContextMenu(messageTextArea, e -> checkKeyCombination(null)));
|
|
|
|
|
|
|
|
// Set the icons of buttons and image views
|
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-07-16 21:13:46 +02:00
|
|
|
messageSearchButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("search", DEFAULT_ICON_SIZE)));
|
2020-07-17 13:56:36 +02:00
|
|
|
clientProfilePic.setImage(IconUtil.loadIconThemeSensitive("user_icon", 43));
|
2020-09-02 10:17:50 +02:00
|
|
|
onlyIfOnlineTooltip.setShowDelay(Duration.millis(250));
|
|
|
|
final var clip = new Rectangle();
|
2020-07-17 13:56:36 +02:00
|
|
|
clip.setWidth(43);
|
|
|
|
clip.setHeight(43);
|
|
|
|
clip.setArcHeight(43);
|
|
|
|
clip.setArcWidth(43);
|
|
|
|
clientProfilePic.setClip(clip);
|
2020-09-01 20:14:02 +02:00
|
|
|
|
2020-09-25 19:19:54 +02:00
|
|
|
chatList.setItems(chats = new FilteredList<>(localDB.getChats()));
|
2020-09-01 20:14:02 +02:00
|
|
|
contactLabel.setText(localDB.getUser().getName());
|
|
|
|
|
|
|
|
initializeSystemCommandsMap();
|
|
|
|
|
2020-08-24 21:54:25 +02:00
|
|
|
Platform.runLater(() -> {
|
2020-09-01 20:57:18 +02:00
|
|
|
final var online = client.isOnline();
|
|
|
|
// no check will be performed in case it has already been disabled - a negative
|
|
|
|
// GroupCreationResult might have been returned
|
|
|
|
if (!newGroupButton.isDisabled()) newGroupButton.setDisable(!online);
|
|
|
|
newContactButton.setDisable(!online);
|
|
|
|
if (online) try {
|
|
|
|
Tooltip.uninstall(contactSpecificOnlineOperations, onlyIfOnlineTooltip);
|
2020-09-02 09:24:46 +02:00
|
|
|
contactSearchTab.setContent(new FXMLLoader().load(getClass().getResourceAsStream("/fxml/ContactSearchTab.fxml")));
|
|
|
|
groupCreationTab.setContent(new FXMLLoader().load(getClass().getResourceAsStream("/fxml/GroupCreationTab.fxml")));
|
2020-09-25 14:29:23 +02:00
|
|
|
} catch (final IOException e) {
|
|
|
|
logger.log(Level.SEVERE, "An error occurred when attempting to load tabs: ", e);
|
2020-09-01 20:14:02 +02:00
|
|
|
}
|
|
|
|
else {
|
2020-09-01 20:57:18 +02:00
|
|
|
Tooltip.install(contactSpecificOnlineOperations, onlyIfOnlineTooltip);
|
|
|
|
updateInfoLabel("You are offline", "info-label-warning");
|
2020-08-24 21:54:25 +02:00
|
|
|
}
|
|
|
|
});
|
2020-09-08 20:41:01 +02:00
|
|
|
}
|
2020-09-01 20:14:02 +02:00
|
|
|
|
2020-09-08 20:41:01 +02:00
|
|
|
@Event(eventType = BackEvent.class)
|
|
|
|
private void onBackEvent() { tabPane.getSelectionModel().select(Tabs.CONTACT_LIST.ordinal()); }
|
|
|
|
|
2020-09-20 14:13:11 +02:00
|
|
|
@Event(includeSubtypes = true)
|
2020-09-16 15:52:58 +02:00
|
|
|
private void onMessage(Message message) {
|
2020-09-08 20:41:01 +02:00
|
|
|
|
|
|
|
// The sender of the message is the recipient of the chat
|
|
|
|
// Exceptions: this user is the sender (sync) or group message (group is
|
|
|
|
// recipient)
|
2020-09-29 18:26:28 +02:00
|
|
|
final var ownMessage = message.getSenderID() == localDB.getUser().getID();
|
|
|
|
final var recipientID = message instanceof GroupMessage || ownMessage ? message.getRecipientID() : message.getSenderID();
|
2020-09-25 11:22:59 +02:00
|
|
|
|
2020-09-08 20:41:01 +02:00
|
|
|
localDB.getChat(recipientID).ifPresent(chat -> {
|
|
|
|
chat.insert(message);
|
2020-09-25 11:22:59 +02:00
|
|
|
|
|
|
|
// Read current chat or increment unread amount
|
2020-09-08 20:41:01 +02:00
|
|
|
if (chat.equals(currentChat)) {
|
2020-09-25 19:19:54 +02:00
|
|
|
currentChat.read(writeProxy);
|
|
|
|
Platform.runLater(this::scrollToMessageListEnd);
|
2020-09-25 11:22:59 +02:00
|
|
|
} else if (!ownMessage && message.getStatus() != MessageStatus.READ) chat.incrementUnreadAmount();
|
2020-09-08 20:41:01 +02:00
|
|
|
|
|
|
|
// Move chat with most recent unread messages to the top
|
|
|
|
Platform.runLater(() -> {
|
|
|
|
chats.getSource().remove(chat);
|
|
|
|
((ObservableList<Chat>) chats.getSource()).add(0, chat);
|
|
|
|
|
|
|
|
if (chat.equals(currentChat)) chatList.getSelectionModel().select(0);
|
2020-06-10 13:05:44 +02:00
|
|
|
});
|
2020-03-29 22:15:05 +02:00
|
|
|
});
|
2020-09-08 20:41:01 +02:00
|
|
|
}
|
2020-03-29 22:15:05 +02:00
|
|
|
|
2020-09-08 20:41:01 +02:00
|
|
|
@Event
|
|
|
|
private void onMessageStatusChange(MessageStatusChange evt) {
|
|
|
|
|
2020-09-25 19:19:54 +02:00
|
|
|
// Update UI if in current chat and the current user was the sender of the
|
|
|
|
// message
|
|
|
|
if (currentChat != null) localDB.getMessage(evt.getID())
|
|
|
|
.filter(msg -> msg.getSenderID() == client.getSender().getID())
|
|
|
|
.ifPresent(msg -> Platform.runLater(messageList::refresh));
|
2020-09-08 20:41:01 +02:00
|
|
|
}
|
|
|
|
|
2020-09-26 12:10:22 +02:00
|
|
|
@Event(eventType = UserStatusChange.class)
|
|
|
|
private void onUserStatusChange() { Platform.runLater(chatList::refresh); }
|
2020-09-08 20:41:01 +02:00
|
|
|
|
|
|
|
@Event
|
|
|
|
private void onContactOperation(ContactOperation operation) {
|
|
|
|
final var contact = operation.get();
|
|
|
|
switch (operation.getOperationType()) {
|
|
|
|
case ADD:
|
|
|
|
if (contact instanceof User) localDB.getUsers().put(contact.getName(), (User) contact);
|
|
|
|
final var chat = contact instanceof User ? new Chat(contact) : new GroupChat(client.getSender(), contact);
|
|
|
|
Platform.runLater(() -> ((ObservableList<Chat>) chats.getSource()).add(0, chat));
|
|
|
|
break;
|
|
|
|
case REMOVE:
|
|
|
|
Platform.runLater(() -> chats.getSource().removeIf(c -> c.getRecipient().equals(contact)));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2020-08-22 18:14:26 +02:00
|
|
|
|
2020-09-08 20:41:01 +02:00
|
|
|
@Event(eventType = NoAttachments.class)
|
|
|
|
private void onNoAttachments() {
|
|
|
|
Platform.runLater(() -> {
|
|
|
|
attachmentButton.setDisable(true);
|
|
|
|
voiceButton.setDisable(true);
|
|
|
|
final var alert = new Alert(AlertType.ERROR);
|
|
|
|
alert.setTitle("No attachments possible");
|
|
|
|
alert.setHeaderText("Your current server does not support attachments.");
|
|
|
|
alert.setContentText("If this is unplanned, please contact your server administrator.");
|
|
|
|
alert.showAndWait();
|
2020-09-02 11:10:05 +02:00
|
|
|
});
|
2020-03-28 10:39:15 +01:00
|
|
|
}
|
|
|
|
|
2020-09-08 20:41:01 +02:00
|
|
|
@Event
|
|
|
|
private void onGroupCreationResult(GroupCreationResult result) { Platform.runLater(() -> newGroupButton.setDisable(!result.get())); }
|
|
|
|
|
|
|
|
@Event(eventType = ThemeChangeEvent.class)
|
|
|
|
private void onThemeChange() {
|
|
|
|
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)));
|
|
|
|
DEFAULT_ATTACHMENT_VIEW_IMAGE = IconUtil.loadIconThemeSensitive("attachment_present", 20);
|
|
|
|
attachmentView.setImage(isCustomAttachmentImage ? attachmentView.getImage() : DEFAULT_ATTACHMENT_VIEW_IMAGE);
|
|
|
|
messageSearchButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("search", DEFAULT_ICON_SIZE)));
|
|
|
|
clientProfilePic.setImage(IconUtil.loadIconThemeSensitive("user_icon", 43));
|
|
|
|
chatList.setCellFactory(new ListCellFactory<>(ChatControl::new));
|
|
|
|
messageList.setCellFactory(MessageListCell::new);
|
2020-09-25 19:19:54 +02:00
|
|
|
// TODO: cache image
|
2020-09-08 20:41:01 +02:00
|
|
|
if (currentChat.getRecipient() instanceof User) recipientProfilePic.setImage(IconUtil.loadIconThemeSensitive("user_icon", 43));
|
|
|
|
else recipientProfilePic.setImage(IconUtil.loadIconThemeSensitive("group_icon", 43));
|
|
|
|
}
|
|
|
|
|
2020-09-24 18:00:59 +02:00
|
|
|
@Event(eventType = Logout.class, priority = 200)
|
|
|
|
private void onLogout() { eventBus.removeListener(this); }
|
|
|
|
|
2020-07-24 11:09:05 +02:00
|
|
|
/**
|
|
|
|
* Initializes all {@code SystemCommands} used in {@code ChatScene}.
|
|
|
|
*
|
|
|
|
* @since Envoy Client v0.2-beta
|
|
|
|
*/
|
|
|
|
private void initializeSystemCommandsMap() {
|
2020-09-23 17:03:32 +02:00
|
|
|
final var builder = new SystemCommandBuilder(messageTextAreaCommands);
|
|
|
|
|
2020-07-24 11:09:05 +02:00
|
|
|
// Do A Barrel roll initialization
|
|
|
|
final var random = new Random();
|
|
|
|
builder.setAction(text -> doABarrelRoll(Integer.parseInt(text.get(0)), Double.parseDouble(text.get(1))))
|
|
|
|
.setDefaults(Integer.toString(random.nextInt(3) + 1), Double.toString(random.nextDouble() * 3 + 1))
|
|
|
|
.setDescription("See for yourself :)")
|
2020-09-23 17:03:32 +02:00
|
|
|
.setNumberOfArguments(2)
|
|
|
|
.build("dabr");
|
2020-09-23 23:11:32 +02:00
|
|
|
|
|
|
|
// Logout initialization
|
|
|
|
builder.setAction(text -> ShutdownHelper.logout()).setNumberOfArguments(0).setDescription("Logs you out.").build("logout");
|
|
|
|
|
|
|
|
// Exit initialization
|
|
|
|
builder.setAction(text -> ShutdownHelper.exit()).setNumberOfArguments(0).setDescription("Exits the program").build("exit", false);
|
|
|
|
builder.build("q");
|
|
|
|
|
|
|
|
// Open settings scene initialization
|
|
|
|
builder.setAction(text -> sceneContext.load(SceneInfo.SETTINGS_SCENE))
|
|
|
|
.setNumberOfArguments(0)
|
|
|
|
.setDescription("Opens the settings screen")
|
|
|
|
.build("settings");
|
2020-09-29 00:28:06 +02:00
|
|
|
|
|
|
|
// Copy text of selection initialization
|
|
|
|
builder.setAction(text -> {
|
|
|
|
final var selectedMessage = messageList.getSelectionModel().getSelectedItem();
|
|
|
|
if (selectedMessage != null) MessageUtil.copyMessageText(selectedMessage);
|
|
|
|
}).setNumberOfArguments(0).setDescription("Copies the text of the currently selected message").build("cp-s");
|
|
|
|
|
|
|
|
// Delete selection initialization
|
|
|
|
builder.setAction(text -> {
|
|
|
|
final var selectedMessage = messageList.getSelectionModel().getSelectedItem();
|
|
|
|
if (selectedMessage != null) MessageUtil.deleteMessage(selectedMessage);
|
|
|
|
}).setNumberOfArguments(0).setDescription("Deletes the currently selected message").build("del-s");
|
|
|
|
|
|
|
|
// Save attachment of selection initialization
|
|
|
|
builder.setAction(text -> {
|
|
|
|
final var selectedMessage = messageList.getSelectionModel().getSelectedItem();
|
|
|
|
if (selectedMessage != null && selectedMessage.hasAttachment()) MessageUtil.saveAttachment(selectedMessage);
|
|
|
|
}).setNumberOfArguments(0).setDescription("Copies the text of the currently selected message").build("save-a-s");
|
2020-07-24 11:09:05 +02: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-07-12 17:10:22 +02:00
|
|
|
private void chatListClicked() {
|
2020-07-13 19:02:40 +02:00
|
|
|
if (chatList.getSelectionModel().isEmpty()) return;
|
|
|
|
|
2020-08-01 09:43:15 +02:00
|
|
|
final var user = chatList.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
|
|
|
|
2020-09-25 19:19:54 +02:00
|
|
|
messageList.setItems(currentChat.getMessages());
|
2020-07-15 21:44:57 +02:00
|
|
|
final var scrollIndex = messageList.getItems().size() - currentChat.getUnreadAmount();
|
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
|
2020-09-25 19:19:54 +02:00
|
|
|
currentChat.read(writeProxy);
|
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-12 17:10:22 +02:00
|
|
|
chatList.refresh();
|
2020-07-16 21:13:46 +02:00
|
|
|
|
2020-07-17 13:56:36 +02:00
|
|
|
if (currentChat != null) {
|
|
|
|
topBarContactLabel.setText(currentChat.getRecipient().getName());
|
|
|
|
if (currentChat.getRecipient() instanceof User) {
|
2020-09-29 18:26:28 +02:00
|
|
|
final var status = ((User) currentChat.getRecipient()).getStatus().toString();
|
2020-07-17 13:56:36 +02:00
|
|
|
topBarStatusLabel.setText(status);
|
|
|
|
topBarStatusLabel.getStyleClass().add(status.toLowerCase());
|
|
|
|
recipientProfilePic.setImage(IconUtil.loadIconThemeSensitive("user_icon", 43));
|
|
|
|
} else {
|
|
|
|
topBarStatusLabel.setText(currentChat.getRecipient().getContacts().size() + " members");
|
|
|
|
recipientProfilePic.setImage(IconUtil.loadIconThemeSensitive("group_icon", 43));
|
|
|
|
}
|
2020-09-29 18:26:28 +02:00
|
|
|
final var clip = new Rectangle();
|
2020-07-17 13:56:36 +02:00
|
|
|
clip.setWidth(43);
|
|
|
|
clip.setHeight(43);
|
|
|
|
clip.setArcHeight(43);
|
|
|
|
clip.setArcWidth(43);
|
|
|
|
recipientProfilePic.setClip(clip);
|
|
|
|
|
|
|
|
messageSearchButton.setVisible(true);
|
2020-07-22 00:03:12 +02:00
|
|
|
}
|
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-09-01 20:14:02 +02:00
|
|
|
private void settingsButtonClicked() { sceneContext.load(SceneContext.SceneInfo.SETTINGS_SCENE); }
|
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
|
2020-09-01 20:14:02 +02:00
|
|
|
private void addContactButtonClicked() { tabPane.getSelectionModel().select(Tabs.CONTACT_SEARCH.ordinal()); }
|
|
|
|
|
2020-08-23 20:15:52 +02:00
|
|
|
@FXML
|
2020-09-01 20:14:02 +02:00
|
|
|
private void groupCreationButtonClicked() { tabPane.getSelectionModel().select(Tabs.GROUP_CREATION.ordinal()); }
|
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 {
|
2020-07-27 12:00:49 +02:00
|
|
|
pendingAttachment = new Attachment(recorder.finish(), "Voice_recording_"
|
|
|
|
+ DateTimeFormatter.ofPattern("yyyy_MM_dd-HH_mm_ss").format(LocalDateTime.now()) + "." + AudioRecorder.FILE_FORMAT,
|
|
|
|
AttachmentType.VOICE);
|
2020-07-03 23:32:22 +02:00
|
|
|
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)
|
2020-08-01 09:43:15 +02:00
|
|
|
var type = AttachmentType.DOCUMENT;
|
2020-07-09 16:05:09 +02:00
|
|
|
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());
|
2020-07-27 12:00:49 +02:00
|
|
|
pendingAttachment = new Attachment(fileBytes, file.getName(), type);
|
2020-07-16 18:23:06 +02:00
|
|
|
checkPostConditions(false);
|
2020-07-10 20:53:28 +02:00
|
|
|
// Setting the preview image as image of the attachmentView
|
2020-09-02 11:10:05 +02:00
|
|
|
if (type == AttachmentType.PICTURE) {
|
2020-07-10 20:53:28 +02:00
|
|
|
attachmentView.setImage(new Image(new ByteArrayInputStream(fileBytes), DEFAULT_ICON_SIZE, DEFAULT_ICON_SIZE, true, true));
|
2020-09-02 11:10:05 +02:00
|
|
|
isCustomAttachmentImage = 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-07-23 17:18:53 +02:00
|
|
|
/**
|
|
|
|
* Rotates every element in our application by {@code rotations}*360° in
|
|
|
|
* {@code an}.
|
|
|
|
*
|
|
|
|
* @param rotations the amount of times the scene is rotated by 360°
|
|
|
|
* @param animationTime the time in seconds that this animation lasts
|
|
|
|
* @since Envoy Client v0.1-beta
|
|
|
|
*/
|
|
|
|
private void doABarrelRoll(int rotations, double animationTime) {
|
2020-08-01 21:40:20 +02:00
|
|
|
// Limiting the rotations and duration
|
|
|
|
rotations = Math.min(rotations, 100000);
|
|
|
|
rotations = Math.max(rotations, 1);
|
|
|
|
animationTime = Math.min(animationTime, 150);
|
|
|
|
animationTime = Math.max(animationTime, 0.25);
|
2020-08-02 20:26:22 +02:00
|
|
|
|
2020-08-01 21:40:20 +02:00
|
|
|
// contains all Node objects in ChatScene
|
2020-08-02 20:26:22 +02:00
|
|
|
final var rotatableNodes = ReflectionUtil.getAllDeclaredNodeVariables(this);
|
2020-07-11 15:23:57 +02:00
|
|
|
for (final var node : rotatableNodes) {
|
2020-07-23 17:18:53 +02:00
|
|
|
// Sets the animation duration to {animationTime}
|
|
|
|
final var rotateTransition = new RotateTransition(Duration.seconds(animationTime), node);
|
|
|
|
// rotates every element {rotations} times
|
|
|
|
rotateTransition.setByAngle(rotations * 360);
|
2020-07-11 15:23:57 +02:00
|
|
|
rotateTransition.play();
|
|
|
|
// This is needed as for some strange reason objects could stop before being
|
|
|
|
// rotated back to 0°
|
|
|
|
rotateTransition.setOnFinished(e -> node.setRotate(0));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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-09-19 17:27:54 +02:00
|
|
|
|
2020-06-14 16:03:02 +02:00
|
|
|
// Checks whether the text is too long
|
2020-06-13 22:38:49 +02:00
|
|
|
messageTextUpdated();
|
2020-09-19 17:27:54 +02:00
|
|
|
|
2020-07-25 17:12:24 +02:00
|
|
|
// Sending an IsTyping event if none has been sent for
|
|
|
|
// IsTyping#millisecondsActive
|
2020-09-25 14:29:23 +02:00
|
|
|
if (client.isOnline() && currentChat.getLastWritingEvent() + IsTyping.millisecondsActive <= System.currentTimeMillis()) {
|
|
|
|
client.send(new IsTyping(getChatID(), currentChat.getRecipient().getID()));
|
2020-07-25 16:26:13 +02:00
|
|
|
currentChat.lastWritingEventWasNow();
|
|
|
|
}
|
2020-09-19 17:27:54 +02:00
|
|
|
|
|
|
|
// KeyPressed will be called before the char has been added to the text, hence
|
|
|
|
// this is needed for the first char
|
2020-09-20 15:27:23 +02:00
|
|
|
if (messageTextArea.getText().length() == 1 && e != null) checkPostConditions(e);
|
2020-09-20 16:06:13 +02:00
|
|
|
|
2020-09-20 15:27:23 +02:00
|
|
|
// This is needed for the messageTA context menu
|
|
|
|
else if (e == null) checkPostConditions(false);
|
2020-06-13 22:38:49 +02:00
|
|
|
}
|
|
|
|
|
2020-07-25 16:26:13 +02:00
|
|
|
/**
|
2020-08-01 09:43:15 +02:00
|
|
|
* Returns the id that should be used to send things to the server: the id of
|
|
|
|
* 'our' {@link User} if the recipient of that object is another User, else the
|
|
|
|
* id of the {@link Group} 'our' user is sending to.
|
2020-07-25 16:26:13 +02:00
|
|
|
*
|
|
|
|
* @return an id that can be sent to the server
|
|
|
|
* @since Envoy Client v0.2-beta
|
|
|
|
*/
|
|
|
|
private long getChatID() {
|
|
|
|
return currentChat.getRecipient() instanceof User ? client.getSender().getID() : currentChat.getRecipient().getID();
|
|
|
|
}
|
|
|
|
|
2020-06-13 22:38:49 +02:00
|
|
|
/**
|
|
|
|
* @param e the keys that have been pressed
|
|
|
|
* @since Envoy Client v0.1-beta
|
|
|
|
*/
|
|
|
|
@FXML
|
|
|
|
private void checkPostConditions(KeyEvent e) {
|
2020-09-20 16:06:13 +02:00
|
|
|
final var enterPressed = e.getCode() == KeyCode.ENTER;
|
|
|
|
final var messagePosted = enterPressed ? settings.isEnterToSend() ? !e.isControlDown() : e.isControlDown() : false;
|
2020-09-19 17:27:54 +02:00
|
|
|
if (messagePosted) {
|
|
|
|
|
|
|
|
// Removing an inserted line break if added by pressing enter
|
|
|
|
final var text = messageTextArea.getText();
|
2020-09-20 16:06:13 +02:00
|
|
|
final var textPosition = messageTextArea.getCaretPosition() - 1;
|
|
|
|
if (!e.isControlDown() && !text.isEmpty() && text.charAt(textPosition) == '\n')
|
|
|
|
messageTextArea.setText(new StringBuilder(text).deleteCharAt(textPosition).toString());
|
|
|
|
}
|
|
|
|
|
|
|
|
// if control is pressed, the enter press is originally invalidated. Here it'll
|
|
|
|
// be inserted again
|
|
|
|
else if (enterPressed && e.isControlDown()) {
|
|
|
|
var caretPosition = messageTextArea.getCaretPosition();
|
|
|
|
messageTextArea.setText(new StringBuilder(messageTextArea.getText()).insert(caretPosition, '\n').toString());
|
|
|
|
messageTextArea.positionCaret(++caretPosition);
|
2020-09-19 17:27:54 +02:00
|
|
|
}
|
|
|
|
checkPostConditions(messagePosted);
|
2020-07-03 23:32:22 +02:00
|
|
|
}
|
|
|
|
|
2020-07-16 18:23:06 +02:00
|
|
|
private void checkPostConditions(boolean postMessage) {
|
2020-06-26 23:16:03 +02:00
|
|
|
if (!postingPermanentlyDisabled) {
|
2020-07-16 18:23:06 +02:00
|
|
|
if (!postButton.isDisabled() && postMessage) 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-09-01 20:57:18 +02:00
|
|
|
updateInfoLabel(noMoreMessaging, "info-label-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-08-01 09:43:15 +02:00
|
|
|
final var currentLength = messageTextArea.getText().length();
|
|
|
|
final var remainingLength = MAX_MESSAGE_LENGTH - currentLength;
|
2020-06-13 22:38:49 +02:00
|
|
|
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-09-01 20:57:18 +02:00
|
|
|
updateInfoLabel("You need to go online to send more messages", "info-label-error");
|
2020-06-26 23:16:03 +02:00
|
|
|
return;
|
|
|
|
}
|
2020-07-25 16:26:13 +02:00
|
|
|
final var text = messageTextArea.getText().strip();
|
2020-09-25 14:29:23 +02:00
|
|
|
if (!messageTextAreaCommands.executeIfAnyPresent(text)) {
|
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(() -> {
|
2020-07-31 18:46:32 +02:00
|
|
|
chats.getSource().remove(currentChat);
|
2020-07-31 22:52:42 +02:00
|
|
|
((ObservableList<Chat>) chats.getSource()).add(0, currentChat);
|
2020-07-12 17:10:22 +02:00
|
|
|
chatList.getSelectionModel().select(0);
|
2020-07-11 14:18:38 +02:00
|
|
|
localDB.getChats().remove(currentChat);
|
|
|
|
localDB.getChats().add(0, currentChat);
|
|
|
|
});
|
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-09-24 18:00:59 +02:00
|
|
|
if (!localDB.getIDGenerator().hasNext() && client.isOnline()) client.requestIDGenerator();
|
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-09-02 11:10:05 +02:00
|
|
|
isCustomAttachmentImage = false;
|
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-08-01 09:43:15 +02:00
|
|
|
* Additionally resets the shown image to {@code DEFAULT_ATTACHMENT_VIEW_IMAGE}
|
|
|
|
* if another image is currently present.
|
2020-07-10 20:53:28 +02:00
|
|
|
*
|
|
|
|
* @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);
|
2020-07-18 09:48:08 +02:00
|
|
|
final var image = attachmentView.getImage();
|
|
|
|
final var messageAttachment = pendingAttachment;
|
2020-06-25 11:17:25 +02:00
|
|
|
postMessage();
|
|
|
|
messageTextArea.setText(messageText);
|
|
|
|
updateRemainingCharsLabel();
|
|
|
|
postButton.setDisable(messageText.isBlank());
|
2020-07-18 09:48:08 +02:00
|
|
|
attachmentView.setImage(image);
|
|
|
|
if (attachmentView.getImage() != null) attachmentView.setVisible(true);
|
|
|
|
pendingAttachment = messageAttachment;
|
2020-03-26 20:23:25 +01:00
|
|
|
}
|
2020-07-31 22:52:42 +02:00
|
|
|
|
2020-09-29 18:26:28 +02:00
|
|
|
/**
|
2020-09-30 13:22:13 +02:00
|
|
|
* Clears the current message selection.
|
2020-09-29 18:26:28 +02:00
|
|
|
*
|
|
|
|
* @since Envoy Client v0.3-beta
|
|
|
|
*/
|
|
|
|
public void clearMessageSelection() { messageList.getSelectionModel().clearSelection(); }
|
|
|
|
|
2020-07-31 18:46:32 +02:00
|
|
|
@FXML
|
|
|
|
private void searchContacts() {
|
|
|
|
chats.setPredicate(contactSearch.getText().isBlank() ? c -> true
|
2020-08-02 20:26:22 +02:00
|
|
|
: c -> c.getRecipient().getName().toLowerCase().contains(contactSearch.getText().toLowerCase()));
|
2020-07-31 18:46:32 +02:00
|
|
|
}
|
2020-03-26 20:23:25 +01:00
|
|
|
}
|