Merged branch 'develop' into f/groups
Contains several bug fixes
This commit is contained in:
		@@ -148,12 +148,22 @@ public abstract class LocalDB {
 | 
			
		||||
	 * Searches for a message by ID.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param id the ID of the message to search for
 | 
			
		||||
	 * @return the message with the corresponding ID, or {@code null} if no message
 | 
			
		||||
	 *         has been found
 | 
			
		||||
	 * @return an optional containing the message
 | 
			
		||||
	 * @since Envoy Client v0.1-beta
 | 
			
		||||
	 */
 | 
			
		||||
	public Message getMessage(long id) {
 | 
			
		||||
		return chats.stream().map(Chat::getMessages).flatMap(List::stream).filter(m -> m.getID() == id).findAny().orElse(null);
 | 
			
		||||
	public Optional<Message> getMessage(long id) {
 | 
			
		||||
		return chats.stream().map(Chat::getMessages).flatMap(List::stream).filter(m -> m.getID() == id).findAny();
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	/**
 | 
			
		||||
	 * Searches for a chat by recipient ID.
 | 
			
		||||
	 * 
 | 
			
		||||
	 * @param recipientID the ID of the chat's recipient
 | 
			
		||||
	 * @return an optional containing the chat
 | 
			
		||||
	 * @since Envoy Client v0.1-beta
 | 
			
		||||
	 */
 | 
			
		||||
	public Optional<Chat> getChat(long recipientID) {
 | 
			
		||||
		return chats.stream().filter(c -> c.getRecipient().getID() == recipientID).findAny();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
 
 | 
			
		||||
@@ -91,7 +91,7 @@ public class Client implements Closeable {
 | 
			
		||||
		SerializationUtils.writeBytesWithLength(credentials, socket.getOutputStream());
 | 
			
		||||
 | 
			
		||||
		// Wait for a maximum of five seconds to acquire the sender object
 | 
			
		||||
		long start = System.currentTimeMillis();
 | 
			
		||||
		final long start = System.currentTimeMillis();
 | 
			
		||||
		while (sender == null) {
 | 
			
		||||
 | 
			
		||||
			// Quit immediately after handshake rejection
 | 
			
		||||
@@ -141,7 +141,7 @@ public class Client implements Closeable {
 | 
			
		||||
		receiver.registerProcessor(MessageStatusChangeEvent.class, new MessageStatusChangeEventProcessor());
 | 
			
		||||
 | 
			
		||||
		// Process user status changes
 | 
			
		||||
		receiver.registerProcessor(UserStatusChangeEvent.class, new UserStatusChangeProcessor(localDB));
 | 
			
		||||
		receiver.registerProcessor(UserStatusChangeEvent.class, eventBus::dispatch);
 | 
			
		||||
 | 
			
		||||
		// Process message ID generation
 | 
			
		||||
		receiver.registerProcessor(IDGenerator.class, localDB::setIDGenerator);
 | 
			
		||||
@@ -162,7 +162,7 @@ public class Client implements Closeable {
 | 
			
		||||
		eventBus.register(SendEvent.class, evt -> {
 | 
			
		||||
			try {
 | 
			
		||||
				sendEvent(evt.get());
 | 
			
		||||
			} catch (IOException e) {
 | 
			
		||||
			} catch (final IOException e) {
 | 
			
		||||
				e.printStackTrace();
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
@@ -221,7 +221,7 @@ public class Client implements Closeable {
 | 
			
		||||
	 */
 | 
			
		||||
	public Map<String, Contact> getUsers() {
 | 
			
		||||
		checkOnline();
 | 
			
		||||
		Map<String, Contact> users = new HashMap<>();
 | 
			
		||||
		final Map<String, Contact> users = new HashMap<>();
 | 
			
		||||
		contacts.forEach(u -> users.put(u.getName(), u));
 | 
			
		||||
		users.put(sender.getName(), sender);
 | 
			
		||||
		return users;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,33 +0,0 @@
 | 
			
		||||
package envoy.client.net;
 | 
			
		||||
 | 
			
		||||
import java.util.function.Consumer;
 | 
			
		||||
 | 
			
		||||
import envoy.client.data.LocalDB;
 | 
			
		||||
import envoy.data.User;
 | 
			
		||||
import envoy.event.EventBus;
 | 
			
		||||
import envoy.event.UserStatusChangeEvent;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Project: <strong>envoy-client</strong><br>
 | 
			
		||||
 * File: <strong>UserStatusChangeProcessor.java</strong><br>
 | 
			
		||||
 * Created: <strong>2 Feb 2020</strong><br>
 | 
			
		||||
 *
 | 
			
		||||
 * @author Leon Hofmeister
 | 
			
		||||
 * @since Envoy Client v0.3-alpha
 | 
			
		||||
 */
 | 
			
		||||
public class UserStatusChangeProcessor implements Consumer<UserStatusChangeEvent> {
 | 
			
		||||
 | 
			
		||||
	private final LocalDB localDB;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @param localDB the local database in which status updates will by applied
 | 
			
		||||
	 * @since Envoy Client v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public UserStatusChangeProcessor(LocalDB localDB) { this.localDB = localDB; }
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void accept(UserStatusChangeEvent evt) {
 | 
			
		||||
		localDB.getUsers().values().stream().filter(u -> u.getID() == evt.getID()).map(User.class::cast).findFirst().get().setStatus(evt.get());
 | 
			
		||||
		EventBus.getInstance().dispatch(evt);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -49,7 +49,7 @@ public class WriteProxy {
 | 
			
		||||
				client.sendMessage(msg);
 | 
			
		||||
 | 
			
		||||
				// Update message state to SENT in localDB
 | 
			
		||||
				localDB.getMessage(msg.getID()).nextStatus();
 | 
			
		||||
				localDB.getMessage(msg.getID()).ifPresent(Message::nextStatus);
 | 
			
		||||
			} catch (IOException e) {
 | 
			
		||||
				logger.log(Level.SEVERE, "Could not send cached message", e);
 | 
			
		||||
			}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,10 @@ package envoy.client.ui;
 | 
			
		||||
import javafx.scene.control.Label;
 | 
			
		||||
import javafx.scene.control.ListCell;
 | 
			
		||||
import javafx.scene.layout.VBox;
 | 
			
		||||
import javafx.scene.paint.Color;
 | 
			
		||||
 | 
			
		||||
import envoy.data.Contact;
 | 
			
		||||
import envoy.data.Group;
 | 
			
		||||
import envoy.data.User;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -20,15 +22,42 @@ public class ContactListCell extends ListCell<Contact> {
 | 
			
		||||
	/**
 | 
			
		||||
	 * Displays the name of a contact. If the contact is a user, their online status
 | 
			
		||||
	 * is displayed as well.
 | 
			
		||||
	 * 
 | 
			
		||||
	 *
 | 
			
		||||
	 * @since Envoy Client v0.1-beta
 | 
			
		||||
	 */
 | 
			
		||||
	@Override
 | 
			
		||||
	protected void updateItem(Contact contact, boolean empty) {
 | 
			
		||||
		super.updateItem(contact, empty);
 | 
			
		||||
		if (!empty && contact != null) {
 | 
			
		||||
			final var name = new Label(contact.getName());
 | 
			
		||||
			setGraphic(contact instanceof User ? new VBox(name, new Label(((User) contact).getStatus().toString())) : new VBox(name));
 | 
			
		||||
		if (empty || contact == null) {
 | 
			
		||||
			setText(null);
 | 
			
		||||
			setGraphic(null);
 | 
			
		||||
		} else {
 | 
			
		||||
			// the infoLabel displays specific contact info, i.e. status of a user or amount
 | 
			
		||||
			// of members in a group
 | 
			
		||||
			Label infoLabel = null;
 | 
			
		||||
			if (contact instanceof User) {
 | 
			
		||||
				// user specific info
 | 
			
		||||
				infoLabel = new Label(((User) contact).getStatus().toString());
 | 
			
		||||
				Color textColor = null;
 | 
			
		||||
				switch (((User) contact).getStatus()) {
 | 
			
		||||
					case ONLINE:
 | 
			
		||||
						textColor = Color.LIMEGREEN;
 | 
			
		||||
						break;
 | 
			
		||||
					case AWAY:
 | 
			
		||||
						textColor = Color.ORANGERED;
 | 
			
		||||
						break;
 | 
			
		||||
					case BUSY:
 | 
			
		||||
						textColor = Color.RED;
 | 
			
		||||
						break;
 | 
			
		||||
					case OFFLINE:
 | 
			
		||||
						textColor = Color.GRAY;
 | 
			
		||||
						break;
 | 
			
		||||
				}
 | 
			
		||||
				infoLabel.setTextFill(textColor);
 | 
			
		||||
			} else
 | 
			
		||||
				// group specific infos
 | 
			
		||||
				infoLabel = new Label(String.valueOf(((Group) contact).getContacts().size()) + " members");
 | 
			
		||||
			setGraphic(new VBox(new Label(contact.getName()), infoLabel));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -45,10 +45,15 @@ public class MessageListCell extends ListCell<Message> {
 | 
			
		||||
	@Override
 | 
			
		||||
	protected void updateItem(Message message, boolean empty) {
 | 
			
		||||
		super.updateItem(message, empty);
 | 
			
		||||
		setGraphic(!empty && message != null ? new HBox(
 | 
			
		||||
		if(empty || message == null) {
 | 
			
		||||
			setText(null);
 | 
			
		||||
			setGraphic(null);
 | 
			
		||||
		} else {
 | 
			
		||||
			setGraphic(new HBox(
 | 
			
		||||
					new VBox(
 | 
			
		||||
					 new Label(dateFormat.format(message.getCreationDate())),
 | 
			
		||||
					 new Label(message.getText())),
 | 
			
		||||
					new Label("", new ImageView(statusImages.get(message.getStatus())))) : null);
 | 
			
		||||
					new Label("", new ImageView(statusImages.get(message.getStatus())))));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -115,6 +115,7 @@ public final class SceneContext {
 | 
			
		||||
			sceneStack.push(scene);
 | 
			
		||||
			stage.setScene(scene);
 | 
			
		||||
			applyCSS();
 | 
			
		||||
			stage.sizeToScene();
 | 
			
		||||
			stage.show();
 | 
			
		||||
		} catch (IOException e) {
 | 
			
		||||
			throw new RuntimeException(e);
 | 
			
		||||
@@ -131,6 +132,7 @@ public final class SceneContext {
 | 
			
		||||
		if (!sceneStack.isEmpty()) {
 | 
			
		||||
			stage.setScene(sceneStack.peek());
 | 
			
		||||
			applyCSS();
 | 
			
		||||
			stage.sizeToScene();
 | 
			
		||||
		}
 | 
			
		||||
		stage.show();
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ import javafx.application.Platform;
 | 
			
		||||
import javafx.collections.FXCollections;
 | 
			
		||||
import javafx.fxml.FXML;
 | 
			
		||||
import javafx.scene.control.*;
 | 
			
		||||
import javafx.scene.control.Alert.AlertType;
 | 
			
		||||
import javafx.scene.input.KeyCode;
 | 
			
		||||
import javafx.scene.input.KeyEvent;
 | 
			
		||||
import javafx.scene.paint.Color;
 | 
			
		||||
@@ -22,9 +23,7 @@ import envoy.client.net.WriteProxy;
 | 
			
		||||
import envoy.client.ui.ContactListCell;
 | 
			
		||||
import envoy.client.ui.MessageListCell;
 | 
			
		||||
import envoy.client.ui.SceneContext;
 | 
			
		||||
import envoy.data.Contact;
 | 
			
		||||
import envoy.data.Message;
 | 
			
		||||
import envoy.data.MessageBuilder;
 | 
			
		||||
import envoy.data.*;
 | 
			
		||||
import envoy.event.EventBus;
 | 
			
		||||
import envoy.event.MessageStatusChangeEvent;
 | 
			
		||||
import envoy.event.UserStatusChangeEvent;
 | 
			
		||||
@@ -74,6 +73,11 @@ public final class ChatScene {
 | 
			
		||||
	private static final Logger		logger				= EnvoyLog.getLogger(ChatScene.class);
 | 
			
		||||
	private static final int		MAX_MESSAGE_LENGTH	= 255;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Initializes the appearance of certain visual components.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @since Envoy Client v0.1-beta
 | 
			
		||||
	 */
 | 
			
		||||
	@FXML
 | 
			
		||||
	private void initialize() {
 | 
			
		||||
 | 
			
		||||
@@ -83,24 +87,36 @@ public final class ChatScene {
 | 
			
		||||
 | 
			
		||||
		// 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();
 | 
			
		||||
			final var message = e.get();
 | 
			
		||||
			localDB.getChat(message.getSenderID()).ifPresent(chat -> {
 | 
			
		||||
				chat.getMessages().add(message);
 | 
			
		||||
 | 
			
		||||
			// Update UI if in current chat
 | 
			
		||||
			if (chat == currentChat) Platform.runLater(() -> messageList.getItems().add(message));
 | 
			
		||||
				// Update UI if in current chat
 | 
			
		||||
				if (chat == currentChat)
 | 
			
		||||
					Platform.runLater(messageList::refresh);
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		// Listen to message status changes
 | 
			
		||||
		eventBus.register(MessageStatusChangeEvent.class, e -> {
 | 
			
		||||
			final var message = localDB.getMessage(e.getID());
 | 
			
		||||
		eventBus.register(MessageStatusChangeEvent.class, e ->
 | 
			
		||||
			localDB.getMessage(e.getID()).ifPresent(message -> {
 | 
			
		||||
			message.setStatus(e.get());
 | 
			
		||||
 | 
			
		||||
			// Update UI if in current chat
 | 
			
		||||
			if (currentChat != null && message.getSenderID() == currentChat.getRecipient().getID()) Platform.runLater(messageList::refresh);
 | 
			
		||||
		});
 | 
			
		||||
		}));
 | 
			
		||||
 | 
			
		||||
		// Listen to user status changes
 | 
			
		||||
		eventBus.register(UserStatusChangeEvent.class, e -> Platform.runLater(userList::refresh));
 | 
			
		||||
		eventBus.register(UserStatusChangeEvent.class, e ->
 | 
			
		||||
			userList.getItems()
 | 
			
		||||
				.stream()
 | 
			
		||||
				.filter(c -> c.getID() == e.getID())
 | 
			
		||||
				.findAny()
 | 
			
		||||
				.ifPresent(u -> {
 | 
			
		||||
					((User) u).setStatus(e.get());
 | 
			
		||||
					Platform.runLater(userList::refresh);
 | 
			
		||||
				})
 | 
			
		||||
		);
 | 
			
		||||
 | 
			
		||||
		// Listen to contacts changes
 | 
			
		||||
		eventBus.register(ContactOperationEvent.class, e -> {
 | 
			
		||||
@@ -121,6 +137,8 @@ public final class ChatScene {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Initializes all necessary data via dependency injection-
 | 
			
		||||
	 *
 | 
			
		||||
	 * @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
 | 
			
		||||
@@ -137,6 +155,11 @@ public final class ChatScene {
 | 
			
		||||
		userList.setItems(FXCollections.observableList(localDB.getChats().stream().map(Chat::getRecipient).collect(Collectors.toList())));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Actions to perform when the list of contacts has been clicked.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @since Envoy Client v0.1-beta
 | 
			
		||||
	 */
 | 
			
		||||
	@FXML
 | 
			
		||||
	private void userListClicked() {
 | 
			
		||||
		final Contact user = userList.getSelectionModel().getSelectedItem();
 | 
			
		||||
@@ -146,10 +169,8 @@ public final class ChatScene {
 | 
			
		||||
			// 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()
 | 
			
		||||
				.filter(c -> c.getRecipient().getID() == user.getID())
 | 
			
		||||
				.findAny()
 | 
			
		||||
			currentChat = localDB
 | 
			
		||||
				.getChat(user.getID())
 | 
			
		||||
				.orElseGet(() -> { final var chat = new Chat(user); localDB.getChats().add(chat); return chat; });
 | 
			
		||||
 | 
			
		||||
			messageList.setItems(FXCollections.observableList(currentChat.getMessages()));
 | 
			
		||||
@@ -161,32 +182,35 @@ public final class ChatScene {
 | 
			
		||||
		messageTextArea.setDisable(currentChat == null);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@FXML
 | 
			
		||||
	private void postButtonClicked() { postMessage(); }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Actions to perform when the Settings Button has been clicked.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @since Envoy Client v0.1-beta
 | 
			
		||||
	 */
 | 
			
		||||
	@FXML
 | 
			
		||||
	private void settingsButtonClicked() {
 | 
			
		||||
		sceneContext.load(SceneContext.SceneInfo.SETTINGS_SCENE);
 | 
			
		||||
		sceneContext.<SettingsScene>getController().initializeData(sceneContext);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Actions to perform when the "Add Contact" - Button has been clicked.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @since Envoy Client v0.1-beta
 | 
			
		||||
	 */
 | 
			
		||||
	@FXML
 | 
			
		||||
	private void addContactButtonClicked() {
 | 
			
		||||
		sceneContext.load(SceneContext.SceneInfo.CONTACT_SEARCH_SCENE);
 | 
			
		||||
		sceneContext.<ContactSearchScene>getController().initializeData(sceneContext, localDB);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Actions to perform when the text was updated in the messageTextArea.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @since Envoy Client v0.1-beta
 | 
			
		||||
	 */
 | 
			
		||||
	@FXML
 | 
			
		||||
	private void messageTextUpdated(KeyEvent e) {
 | 
			
		||||
		// TODO: Letting go of ctrl automatically posts a message. Needs to be fixed
 | 
			
		||||
		// soon.
 | 
			
		||||
 | 
			
		||||
		// Automatic sending of messages via (ctrl +) enter
 | 
			
		||||
		if (!postButton.isDisabled() && settings.isEnterToSend() && e.getCode() == KeyCode.ENTER
 | 
			
		||||
				|| !settings.isEnterToSend() && e.getCode() == KeyCode.CONTROL)
 | 
			
		||||
			postMessage();
 | 
			
		||||
		else postButton.setDisable(messageTextArea.getText().isBlank() || currentChat == null);
 | 
			
		||||
 | 
			
		||||
	private void messageTextUpdated() {
 | 
			
		||||
		// 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));
 | 
			
		||||
@@ -201,33 +225,34 @@ public final class ChatScene {
 | 
			
		||||
		remainingChars.setTextFill(Color.rgb(currentLength, remainingLength, 0, 1));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Actions to perform when a key has been entered.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param e the Keys that have been entered
 | 
			
		||||
	 * @since Envoy Client v0.1-beta
 | 
			
		||||
	 */
 | 
			
		||||
	@FXML
 | 
			
		||||
	private void checkKeyCombination(KeyEvent e) {
 | 
			
		||||
		// Automatic sending of messages via (ctrl +) enter
 | 
			
		||||
		if (!postButton.isDisabled() && settings.isEnterToSend() && e.getCode() == KeyCode.ENTER
 | 
			
		||||
				|| !settings.isEnterToSend() && e.getCode() == KeyCode.ENTER && e.isControlDown())
 | 
			
		||||
			postMessage();
 | 
			
		||||
		postButton.setDisable(messageTextArea.getText().isBlank() || currentChat == null);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Sends a new message to the server based on the text entered in the
 | 
			
		||||
	 * messageTextArea.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @since Envoy Client v0.1-beta
 | 
			
		||||
	 */
 | 
			
		||||
	@FXML
 | 
			
		||||
	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);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 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.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param message the message to send
 | 
			
		||||
	 * @since Envoy Client v0.1-beta
 | 
			
		||||
	 */
 | 
			
		||||
	private void sendMessage(Message message) {
 | 
			
		||||
		try {
 | 
			
		||||
			// Create and send message
 | 
			
		||||
			final var message = new MessageBuilder(localDB.getUser().getID(), currentChat.getRecipient().getID(), localDB.getIDGenerator())
 | 
			
		||||
				.setText(messageTextArea.getText().strip())
 | 
			
		||||
				.build();
 | 
			
		||||
 | 
			
		||||
			// Send message
 | 
			
		||||
			writeProxy.writeMessage(message);
 | 
			
		||||
@@ -240,6 +265,11 @@ public final class ChatScene {
 | 
			
		||||
 | 
			
		||||
		} catch (final IOException e) {
 | 
			
		||||
			logger.log(Level.SEVERE, "Error sending message", e);
 | 
			
		||||
			new Alert(AlertType.ERROR, "An error occured while sending the message!").showAndWait();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Clear text field and disable post button
 | 
			
		||||
		messageTextArea.setText("");
 | 
			
		||||
		postButton.setDisable(true);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,7 @@ import envoy.util.EnvoyLog;
 | 
			
		||||
 * Project: <strong>envoy-client</strong><br>
 | 
			
		||||
 * File: <strong>LoginDialog.java</strong><br>
 | 
			
		||||
 * Created: <strong>03.04.2020</strong><br>
 | 
			
		||||
 * 
 | 
			
		||||
 *
 | 
			
		||||
 * @author Kai S. K. Engelbart
 | 
			
		||||
 * @since Envoy Client v0.1-beta
 | 
			
		||||
 */
 | 
			
		||||
@@ -72,7 +72,7 @@ public final class LoginScene {
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Loads the login dialog using the FXML file {@code LoginDialog.fxml}.
 | 
			
		||||
	 * 
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param client               the client used to perform the handshake
 | 
			
		||||
	 * @param localDB              the local database used for offline login
 | 
			
		||||
	 * @param receivedMessageCache the cache storing messages received during
 | 
			
		||||
@@ -104,9 +104,7 @@ public final class LoginScene {
 | 
			
		||||
		if (registerCheckBox.isSelected() && !passwordField.getText().equals(repeatPasswordField.getText())) {
 | 
			
		||||
			clearPasswordFields();
 | 
			
		||||
			new Alert(AlertType.ERROR, "The entered password is unequal to the repeated one").showAndWait();
 | 
			
		||||
		} else {
 | 
			
		||||
			performHandshake(new LoginCredentials(userTextField.getText(), passwordField.getText().toCharArray(), registerCheckBox.isSelected()));
 | 
			
		||||
		}
 | 
			
		||||
		} else performHandshake(new LoginCredentials(userTextField.getText(), passwordField.getText().toCharArray(), registerCheckBox.isSelected()));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@FXML
 | 
			
		||||
@@ -120,7 +118,6 @@ public final class LoginScene {
 | 
			
		||||
		// Make repeat password field and label visible / invisible
 | 
			
		||||
		repeatPasswordField.setVisible(registerCheckBox.isSelected());
 | 
			
		||||
		repeatPasswordLabel.setVisible(registerCheckBox.isSelected());
 | 
			
		||||
 | 
			
		||||
		clearPasswordFields();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -148,12 +145,12 @@ public final class LoginScene {
 | 
			
		||||
		try {
 | 
			
		||||
			// Try entering offline mode
 | 
			
		||||
			localDB.loadUsers();
 | 
			
		||||
			User clientUser = (User) localDB.getUsers().get(credentials.getIdentifier());
 | 
			
		||||
			final User clientUser = (User) localDB.getUsers().get(credentials.getIdentifier());
 | 
			
		||||
			if (clientUser == null) throw new EnvoyException("Could not enter offline mode: user name unknown");
 | 
			
		||||
			client.setSender(clientUser);
 | 
			
		||||
			new Alert(AlertType.WARNING, "A connection to the server could not be established. Starting in offline mode.").showAndWait();
 | 
			
		||||
			loadChatScene();
 | 
			
		||||
		} catch (Exception e) {
 | 
			
		||||
		} catch (final Exception e) {
 | 
			
		||||
			new Alert(AlertType.ERROR, "Client error: " + e).showAndWait();
 | 
			
		||||
			System.exit(1);
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,8 +4,8 @@
 | 
			
		||||
 * Project: <strong>envoy-client</strong><br>
 | 
			
		||||
 * File: <strong>package-info.java</strong><br>
 | 
			
		||||
 * Created: <strong>08.06.2020</strong><br>
 | 
			
		||||
 * 
 | 
			
		||||
 *
 | 
			
		||||
 * @author Kai S. K. Engelbart
 | 
			
		||||
 * @since Envoy Client v0.1-beta
 | 
			
		||||
 */
 | 
			
		||||
package envoy.client.ui.controller;
 | 
			
		||||
package envoy.client.ui.controller;
 | 
			
		||||
 
 | 
			
		||||
@@ -8,13 +8,14 @@ import javafx.scene.layout.VBox;
 | 
			
		||||
import envoy.client.data.Settings;
 | 
			
		||||
import envoy.client.data.SettingsItem;
 | 
			
		||||
import envoy.client.event.ThemeChangeEvent;
 | 
			
		||||
import envoy.data.User.UserStatus;
 | 
			
		||||
import envoy.event.EventBus;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Project: <strong>envoy-client</strong><br>
 | 
			
		||||
 * File: <strong>GeneralSettingsPane.java</strong><br>
 | 
			
		||||
 * Created: <strong>18.04.2020</strong><br>
 | 
			
		||||
 * 
 | 
			
		||||
 *
 | 
			
		||||
 * @author Kai S. K. Engelbart
 | 
			
		||||
 * @since Envoy Client v0.1-beta
 | 
			
		||||
 */
 | 
			
		||||
@@ -27,7 +28,7 @@ public class GeneralSettingsPane extends SettingsPane {
 | 
			
		||||
	 */
 | 
			
		||||
	public GeneralSettingsPane() {
 | 
			
		||||
		super("General");
 | 
			
		||||
		var vbox = new VBox();
 | 
			
		||||
		final var vbox = new VBox();
 | 
			
		||||
 | 
			
		||||
		// TODO: Support other value types
 | 
			
		||||
		List.of("onCloseMode", "enterToSend")
 | 
			
		||||
@@ -36,7 +37,7 @@ public class GeneralSettingsPane extends SettingsPane {
 | 
			
		||||
			.map(i -> new SettingsCheckbox((SettingsItem<Boolean>) i))
 | 
			
		||||
			.forEach(vbox.getChildren()::add);
 | 
			
		||||
 | 
			
		||||
		var combobox = new ComboBox<String>();
 | 
			
		||||
		final var combobox = new ComboBox<String>();
 | 
			
		||||
		combobox.getItems().add("dark");
 | 
			
		||||
		combobox.getItems().add("light");
 | 
			
		||||
		combobox.setValue(settings.getCurrentTheme());
 | 
			
		||||
@@ -44,6 +45,13 @@ public class GeneralSettingsPane extends SettingsPane {
 | 
			
		||||
				e -> { settings.setCurrentTheme(combobox.getValue()); EventBus.getInstance().dispatch(new ThemeChangeEvent(combobox.getValue())); });
 | 
			
		||||
		vbox.getChildren().add(combobox);
 | 
			
		||||
 | 
			
		||||
		final var statusComboBox = new ComboBox<UserStatus>();
 | 
			
		||||
		statusComboBox.getItems().setAll(UserStatus.values());
 | 
			
		||||
		statusComboBox.setValue(UserStatus.ONLINE);
 | 
			
		||||
		// TODO add action when value is changed
 | 
			
		||||
		statusComboBox.setOnAction(e -> {});
 | 
			
		||||
		vbox.getChildren().add(statusComboBox);
 | 
			
		||||
 | 
			
		||||
		getChildren().add(vbox);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
.button{
 | 
			
		||||
	-fx-background-color: rgb(105,0,153);
 | 
			
		||||
	-fx-text-fill: white;
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,3 @@
 | 
			
		||||
.button{
 | 
			
		||||
	-fx-background-color: snow;
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -26,8 +26,8 @@
 | 
			
		||||
		<Label fx:id="contactLabel" prefHeight="16.0" prefWidth="250.0" text="Select a contact to chat with" GridPane.columnSpan="2" />
 | 
			
		||||
		<Button fx:id="settingsButton" mnemonicParsing="true" onAction="#settingsButtonClicked" text="_Settings" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.valignment="CENTER" />
 | 
			
		||||
		<ListView fx:id="messageList" prefHeight="257.0" prefWidth="155.0" GridPane.columnIndex="1" GridPane.columnSpan="2" GridPane.rowIndex="1" GridPane.rowSpan="2" />
 | 
			
		||||
		<Button fx:id="postButton" defaultButton="true" disable="true" mnemonicParsing="true" onAction="#postButtonClicked" prefHeight="10.0" prefWidth="65.0" text="_Post" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.rowIndex="3" GridPane.valignment="CENTER" />
 | 
			
		||||
      	<TextArea fx:id="messageTextArea" disable="true" onInputMethodTextChanged="#messageTextUpdated" onKeyPressed="#messageTextUpdated" onKeyTyped="#messageTextUpdated" prefHeight="200.0" prefWidth="200.0" wrapText="true" GridPane.columnIndex="1" GridPane.rowIndex="3" />
 | 
			
		||||
		<Button fx:id="postButton" defaultButton="true" disable="true" mnemonicParsing="true" onAction="#postMessage" prefHeight="10.0" prefWidth="65.0" text="_Post" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.rowIndex="3" GridPane.valignment="CENTER" />
 | 
			
		||||
      	<TextArea fx:id="messageTextArea" disable="true" onInputMethodTextChanged="#messageTextUpdated" onKeyPressed="#checkKeyCombination" onKeyTyped="#messageTextUpdated" prefHeight="200.0" prefWidth="200.0" wrapText="true" GridPane.columnIndex="1" GridPane.rowIndex="3" />
 | 
			
		||||
      <Button mnemonicParsing="true" onAction="#addContactButtonClicked" text="_Add Contacts" GridPane.halignment="CENTER" GridPane.rowIndex="3" GridPane.valignment="CENTER">
 | 
			
		||||
         <padding>
 | 
			
		||||
            <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user