2020-06-08 10:36:30 +02:00
|
|
|
package envoy.client.ui.controller;
|
2020-03-26 20:23:25 +01:00
|
|
|
|
2020-03-28 15:32:24 +01:00
|
|
|
import java.io.IOException;
|
|
|
|
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.*;
|
|
|
|
import javafx.scene.input.KeyCode;
|
|
|
|
import javafx.scene.input.KeyEvent;
|
2020-06-07 23:28:25 +02:00
|
|
|
import javafx.scene.paint.Color;
|
2020-04-18 19:46:04 +02:00
|
|
|
|
2020-03-28 15:32:24 +01:00
|
|
|
import envoy.client.data.Chat;
|
|
|
|
import envoy.client.data.LocalDB;
|
2020-04-19 22:01:12 +02:00
|
|
|
import envoy.client.data.Settings;
|
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-08 10:36:30 +02:00
|
|
|
import envoy.client.ui.MessageListCell;
|
|
|
|
import envoy.client.ui.SceneContext;
|
|
|
|
import envoy.client.ui.UserListCell;
|
2020-04-02 22:03:43 +02:00
|
|
|
import envoy.data.Contact;
|
2020-03-28 15:32:24 +01:00
|
|
|
import envoy.data.Message;
|
|
|
|
import envoy.data.MessageBuilder;
|
2020-03-29 22:15:05 +02:00
|
|
|
import envoy.event.EventBus;
|
|
|
|
import envoy.event.MessageStatusChangeEvent;
|
|
|
|
import envoy.event.UserStatusChangeEvent;
|
2020-06-07 20:46:04 +02:00
|
|
|
import envoy.event.contact.ContactOperationEvent;
|
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-06-08 10:36:30 +02:00
|
|
|
public final class ChatScene {
|
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-04-02 22:03:43 +02:00
|
|
|
private ListView<Contact> userList;
|
2020-03-28 15:32:24 +01:00
|
|
|
|
|
|
|
@FXML
|
|
|
|
private Button postButton;
|
|
|
|
|
|
|
|
@FXML
|
|
|
|
private Button settingsButton;
|
|
|
|
|
|
|
|
@FXML
|
|
|
|
private TextArea messageTextArea;
|
|
|
|
|
2020-06-07 23:28:25 +02:00
|
|
|
@FXML
|
|
|
|
private Label remainingChars;
|
|
|
|
|
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
|
|
|
|
|
|
|
private Chat currentChat;
|
2020-03-28 10:39:15 +01:00
|
|
|
|
2020-06-07 23:28:25 +02:00
|
|
|
private static final Settings settings = Settings.getInstance();
|
|
|
|
private static final EventBus eventBus = EventBus.getInstance();
|
2020-06-08 10:36:30 +02:00
|
|
|
private static final Logger logger = EnvoyLog.getLogger(ChatScene.class);
|
2020-06-07 23:28:25 +02:00
|
|
|
private static final int MAX_MESSAGE_LENGTH = 255;
|
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 void initialize() {
|
2020-03-29 22:15:05 +02:00
|
|
|
|
|
|
|
// Initialize message and user rendering
|
2020-03-28 10:39:15 +01:00
|
|
|
messageList.setCellFactory(listView -> new MessageListCell());
|
|
|
|
userList.setCellFactory(listView -> new UserListCell());
|
2020-03-29 22:15:05 +02:00
|
|
|
|
|
|
|
// Listen to received messages
|
|
|
|
eventBus.register(MessageCreationEvent.class, e -> {
|
|
|
|
final var message = e.get();
|
|
|
|
final var chat = localDB.getChats().stream().filter(c -> c.getRecipient().getID() == message.getSenderID()).findAny().get();
|
|
|
|
|
|
|
|
// Update UI if in current chat
|
2020-04-09 22:30:24 +02:00
|
|
|
if (chat == currentChat) Platform.runLater(() -> messageList.getItems().add(message));
|
2020-03-29 22:15:05 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
// Listen to message status changes
|
|
|
|
eventBus.register(MessageStatusChangeEvent.class, e -> {
|
|
|
|
final var message = localDB.getMessage(e.getID());
|
|
|
|
message.setStatus(e.get());
|
|
|
|
|
|
|
|
// Update UI if in current chat
|
2020-04-09 22:30:24 +02:00
|
|
|
if (currentChat != null && message.getSenderID() == currentChat.getRecipient().getID()) Platform.runLater(() -> messageList.refresh());
|
2020-03-29 22:15:05 +02:00
|
|
|
});
|
|
|
|
// Listen to user status changes
|
2020-06-07 20:46:04 +02:00
|
|
|
eventBus.register(UserStatusChangeEvent.class, e -> Platform.runLater(userList::refresh));
|
|
|
|
|
|
|
|
// Listen to contacts changes
|
|
|
|
eventBus.register(ContactOperationEvent.class, e -> {
|
|
|
|
final var contact = e.get();
|
|
|
|
Platform.runLater(() -> {
|
|
|
|
switch (e.getOperationType()) {
|
|
|
|
case ADD:
|
|
|
|
localDB.getUsers().put(contact.getName(), contact);
|
|
|
|
localDB.getChats().add(new Chat(contact));
|
|
|
|
userList.getItems().add(contact);// TODO might cause contact duplication
|
|
|
|
break;
|
|
|
|
case REMOVE:
|
|
|
|
// TODO add deletion capability
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
2020-03-28 10:39:15 +01:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
|
|
// TODO: handle offline mode
|
2020-04-02 22:03:43 +02:00
|
|
|
userList.setItems(FXCollections.observableList(localDB.getUser().getContacts().stream().collect(Collectors.toList())));
|
2020-03-28 15:32:24 +01:00
|
|
|
}
|
|
|
|
|
2020-03-26 20:23:25 +01:00
|
|
|
@FXML
|
2020-03-28 15:32:24 +01:00
|
|
|
private void userListClicked() {
|
2020-04-02 22:03:43 +02:00
|
|
|
final Contact user = userList.getSelectionModel().getSelectedItem();
|
2020-03-29 09:16:29 +02:00
|
|
|
if (user != null && (currentChat == null || user.getID() != currentChat.getRecipient().getID())) {
|
2020-03-28 15:32:24 +01:00
|
|
|
contactLabel.setText(user.getName());
|
|
|
|
|
|
|
|
// LEON: JFC <===> JAVA FRIED CHICKEN <=/=> Java Foundation Classes
|
|
|
|
|
|
|
|
// Load the chat or create a new one and add it to the LocalDB
|
|
|
|
currentChat = localDB.getChats()
|
|
|
|
.stream()
|
2020-03-29 22:15:05 +02:00
|
|
|
.filter(c -> c.getRecipient().getID() == user.getID())
|
2020-03-28 15:32:24 +01:00
|
|
|
.findAny()
|
2020-06-07 20:46:04 +02:00
|
|
|
.orElseGet(() -> { final var chat = new Chat(user); localDB.getChats().add(chat); return chat; });
|
2020-03-30 21:27:07 +02:00
|
|
|
|
|
|
|
messageList.setItems(FXCollections.observableList(currentChat.getMessages()));
|
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-07 20:46:04 +02:00
|
|
|
messageTextArea.setDisable(currentChat == null);
|
2020-03-26 20:23:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@FXML
|
2020-03-28 15:32:24 +01:00
|
|
|
private void postButtonClicked() { postMessage(); }
|
|
|
|
|
|
|
|
@FXML
|
2020-04-18 19:46:04 +02:00
|
|
|
private void settingsButtonClicked() {
|
2020-06-06 18:30:09 +02:00
|
|
|
try {
|
|
|
|
sceneContext.load(SceneContext.SceneInfo.SETTINGS_SCENE);
|
2020-06-08 10:36:30 +02:00
|
|
|
sceneContext.<SettingsScene>getController().initializeData(sceneContext);
|
2020-06-07 20:46:04 +02:00
|
|
|
} catch (final IOException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@FXML
|
|
|
|
private void addContactButtonClicked() {
|
|
|
|
try {
|
|
|
|
sceneContext.load(SceneContext.SceneInfo.CONTACT_SEARCH_SCENE);
|
2020-06-08 10:36:30 +02:00
|
|
|
sceneContext.<ContactSearchScene>getController().initializeData(sceneContext);
|
2020-06-07 20:46:04 +02:00
|
|
|
} catch (final IOException e) {
|
2020-06-06 18:30:09 +02:00
|
|
|
e.printStackTrace();
|
|
|
|
}
|
2020-04-18 19:46:04 +02:00
|
|
|
}
|
2020-03-28 15:32:24 +01:00
|
|
|
|
|
|
|
@FXML
|
|
|
|
private void messageTextUpdated(KeyEvent e) {
|
2020-06-07 23:28:25 +02:00
|
|
|
// TODO: Letting go of ctrl automatically posts a message. Needs to be fixed
|
|
|
|
// soon.
|
|
|
|
|
|
|
|
// Automatic sending of messages via (ctrl +) enter
|
2020-06-06 15:29:29 +02:00
|
|
|
if (!postButton.isDisabled() && settings.isEnterToSend() && e.getCode() == KeyCode.ENTER
|
|
|
|
|| !settings.isEnterToSend() && e.getCode() == KeyCode.CONTROL)
|
|
|
|
postMessage();
|
|
|
|
else postButton.setDisable(messageTextArea.getText().isBlank() || currentChat == null);
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Redesigning the remainingChars - Label
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sends a new message to the server based on the text entered in the
|
|
|
|
* messageTextArea.
|
|
|
|
*
|
|
|
|
* @since Envoy Client v0.1-beta
|
|
|
|
*/
|
|
|
|
private void postMessage() {
|
|
|
|
|
|
|
|
// Create and send message
|
|
|
|
sendMessage(new MessageBuilder(localDB.getUser().getID(), currentChat.getRecipient().getID(), localDB.getIDGenerator())
|
|
|
|
.setText(messageTextArea.getText().strip())
|
|
|
|
.build());
|
|
|
|
|
|
|
|
// Clear text field and disable post button
|
|
|
|
messageTextArea.setText("");
|
|
|
|
postButton.setDisable(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-03-29 09:16:29 +02:00
|
|
|
* Sends a message to the server and appends it to the current chat. If all
|
|
|
|
* message IDs have been used, a new ID generator is requested.
|
2020-03-28 15:32:24 +01:00
|
|
|
*
|
|
|
|
* @param message the message to send
|
|
|
|
* @since Envoy Client v0.1-beta
|
|
|
|
*/
|
|
|
|
private void sendMessage(Message message) {
|
|
|
|
try {
|
|
|
|
|
|
|
|
// Send message
|
|
|
|
writeProxy.writeMessage(message);
|
|
|
|
|
|
|
|
// Add message to LocalDB and update UI
|
|
|
|
messageList.getItems().add(message);
|
|
|
|
|
|
|
|
// 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-03-28 15:32:24 +01:00
|
|
|
logger.log(Level.SEVERE, "Error sending message", e);
|
|
|
|
}
|
2020-03-26 20:23:25 +01:00
|
|
|
}
|
|
|
|
}
|