Merge branch 'develop' into f/new_ui
Conflicts: client/src/main/java/envoy/client/ui/controller/LoginScene.java
This commit is contained in:
		@@ -5,10 +5,8 @@ import static java.util.function.Function.identity;
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.util.logging.Level;
 | 
			
		||||
 | 
			
		||||
import envoy.client.ui.Startup;
 | 
			
		||||
import envoy.data.Config;
 | 
			
		||||
import envoy.data.ConfigItem;
 | 
			
		||||
import envoy.data.LoginCredentials;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Implements a configuration specific to the Envoy Client with default values
 | 
			
		||||
@@ -40,8 +38,8 @@ public class ClientConfig extends Config {
 | 
			
		||||
		items.put("localDB", new ConfigItem<>("localDB", "db", File::new, new File("localDB"), true));
 | 
			
		||||
		items.put("ignoreLocalDB", new ConfigItem<>("ignoreLocalDB", "nodb", Boolean::parseBoolean, false, false));
 | 
			
		||||
		items.put("homeDirectory", new ConfigItem<>("homeDirectory", "h", File::new, new File(System.getProperty("user.home"), ".envoy"), true));
 | 
			
		||||
		items.put("fileLevelBarrier", new ConfigItem<>("fileLevelBarrier", "fb", Level::parse, Level.CONFIG, true));
 | 
			
		||||
		items.put("consoleLevelBarrier", new ConfigItem<>("consoleLevelBarrier", "cb", Level::parse, Level.FINEST, true));
 | 
			
		||||
		items.put("fileLevelBarrier", new ConfigItem<>("fileLevelBarrier", "fb", Level::parse, Level.OFF, true));
 | 
			
		||||
		items.put("consoleLevelBarrier", new ConfigItem<>("consoleLevelBarrier", "cb", Level::parse, Level.OFF, true));
 | 
			
		||||
		items.put("user", new ConfigItem<>("user", "u", identity()));
 | 
			
		||||
		items.put("password", new ConfigItem<>("password", "pw", identity()));
 | 
			
		||||
	}
 | 
			
		||||
@@ -105,11 +103,4 @@ public class ClientConfig extends Config {
 | 
			
		||||
	 * @since Envoy Client v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public boolean hasLoginCredentials() { return getUser() != null && getPassword() != null; }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return login credentials for the specified user name and password, without
 | 
			
		||||
	 *         the registration option
 | 
			
		||||
	 * @since Envoy Client v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public LoginCredentials getLoginCredentials() { return new LoginCredentials(getUser(), getPassword(), false, Startup.VERSION); }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
package envoy.client.data;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.time.LocalDateTime;
 | 
			
		||||
import java.time.Instant;
 | 
			
		||||
 | 
			
		||||
import envoy.client.net.WriteProxy;
 | 
			
		||||
import envoy.data.Contact;
 | 
			
		||||
@@ -46,7 +46,7 @@ public class GroupChat extends Chat {
 | 
			
		||||
				else {
 | 
			
		||||
					gmsg.getMemberStatuses().replace(sender.getID(), MessageStatus.READ);
 | 
			
		||||
					writeProxy
 | 
			
		||||
						.writeMessageStatusChange(new GroupMessageStatusChange(gmsg.getID(), MessageStatus.READ, LocalDateTime.now(), sender.getID()));
 | 
			
		||||
						.writeMessageStatusChange(new GroupMessageStatusChange(gmsg.getID(), MessageStatus.READ, Instant.now(), sender.getID()));
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
package envoy.client.data;
 | 
			
		||||
 | 
			
		||||
import java.time.Instant;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
 | 
			
		||||
import envoy.data.*;
 | 
			
		||||
@@ -20,11 +21,12 @@ import envoy.event.NameChange;
 | 
			
		||||
 */
 | 
			
		||||
public abstract class LocalDB {
 | 
			
		||||
 | 
			
		||||
	protected User					user;
 | 
			
		||||
	protected Map<String, Contact>	users		= new HashMap<>();
 | 
			
		||||
	protected List<Chat>			chats		= new ArrayList<>();
 | 
			
		||||
	protected IDGenerator			idGenerator;
 | 
			
		||||
	protected CacheMap				cacheMap	= new CacheMap();
 | 
			
		||||
	protected User				user;
 | 
			
		||||
	protected Map<String, User>	users		= new HashMap<>();
 | 
			
		||||
	protected List<Chat>		chats		= new ArrayList<>();
 | 
			
		||||
	protected IDGenerator		idGenerator;
 | 
			
		||||
	protected CacheMap			cacheMap	= new CacheMap();
 | 
			
		||||
	protected Instant			lastSync	= Instant.EPOCH;
 | 
			
		||||
 | 
			
		||||
	{
 | 
			
		||||
		cacheMap.put(Message.class, new Cache<>());
 | 
			
		||||
@@ -42,10 +44,11 @@ public abstract class LocalDB {
 | 
			
		||||
	 * Stores all users. If the client user is specified, their chats will be stored
 | 
			
		||||
	 * as well. The message id generator will also be saved if present.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param isOnline determines which {@code lastSync} time stamp is saved
 | 
			
		||||
	 * @throws Exception if the saving process failed
 | 
			
		||||
	 * @since Envoy Client v0.3-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public void save() throws Exception {}
 | 
			
		||||
	public void save(boolean isOnline) throws Exception {}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Loads all user data.
 | 
			
		||||
@@ -77,7 +80,7 @@ public abstract class LocalDB {
 | 
			
		||||
	 * @since Envoy Client v0.1-beta
 | 
			
		||||
	 */
 | 
			
		||||
	public void synchronize() {
 | 
			
		||||
		user.getContacts().stream().filter(u -> u instanceof User && !users.containsKey(u.getName())).forEach(u -> users.put(u.getName(), u));
 | 
			
		||||
		user.getContacts().stream().filter(u -> u instanceof User && !users.containsKey(u.getName())).forEach(u -> users.put(u.getName(), (User) u));
 | 
			
		||||
		users.put(user.getName(), user);
 | 
			
		||||
 | 
			
		||||
		// Synchronize user status data
 | 
			
		||||
@@ -98,7 +101,7 @@ public abstract class LocalDB {
 | 
			
		||||
	 *         user names as keys
 | 
			
		||||
	 * @since Envoy Client v0.2-alpha
 | 
			
		||||
	 */
 | 
			
		||||
	public Map<String, Contact> getUsers() { return users; }
 | 
			
		||||
	public Map<String, User> getUsers() { return users; }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return all saved {@link Chat} objects that list the client user as the
 | 
			
		||||
@@ -148,6 +151,12 @@ public abstract class LocalDB {
 | 
			
		||||
	 */
 | 
			
		||||
	public CacheMap getCacheMap() { return cacheMap; }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * @return the time stamp when the database was last saved
 | 
			
		||||
	 * @since Envoy Client v0.2-beta
 | 
			
		||||
	 */
 | 
			
		||||
	public Instant getLastSync() { return lastSync; }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Searches for a message by ID.
 | 
			
		||||
	 *
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
package envoy.client.data;
 | 
			
		||||
 | 
			
		||||
import java.io.*;
 | 
			
		||||
import java.time.Instant;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
 | 
			
		||||
@@ -26,7 +27,7 @@ public final class PersistentLocalDB extends LocalDB {
 | 
			
		||||
	/**
 | 
			
		||||
	 * Constructs an empty local database. To serialize any user-specific data to
 | 
			
		||||
	 * the file system, call {@link PersistentLocalDB#initializeUserStorage()} first
 | 
			
		||||
	 * and then {@link PersistentLocalDB#save()}.
 | 
			
		||||
	 * and then {@link PersistentLocalDB#save(boolean)}.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param dbDir the directory in which to persist data
 | 
			
		||||
	 * @throws IOException if {@code dbDir} is a file (and not a directory)
 | 
			
		||||
@@ -57,12 +58,12 @@ public final class PersistentLocalDB extends LocalDB {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void save() throws IOException {
 | 
			
		||||
	public void save(boolean isOnline) throws IOException {
 | 
			
		||||
		// Save users
 | 
			
		||||
		SerializationUtils.write(usersFile, users);
 | 
			
		||||
 | 
			
		||||
		// Save user data
 | 
			
		||||
		if (user != null) SerializationUtils.write(userFile, chats, cacheMap);
 | 
			
		||||
		// Save user data and last sync time stamp
 | 
			
		||||
		if (user != null) SerializationUtils.write(userFile, chats, cacheMap, isOnline ? Instant.now() : lastSync);
 | 
			
		||||
 | 
			
		||||
		// Save id generator
 | 
			
		||||
		if (hasIDGenerator()) SerializationUtils.write(idGeneratorFile, idGenerator);
 | 
			
		||||
@@ -76,6 +77,7 @@ public final class PersistentLocalDB extends LocalDB {
 | 
			
		||||
		try (var in = new ObjectInputStream(new FileInputStream(userFile))) {
 | 
			
		||||
			chats		= (ArrayList<Chat>) in.readObject();
 | 
			
		||||
			cacheMap	= (CacheMap) in.readObject();
 | 
			
		||||
			lastSync	= (Instant) in.readObject();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,11 @@
 | 
			
		||||
package envoy.client.net;
 | 
			
		||||
 | 
			
		||||
import java.util.function.Consumer;
 | 
			
		||||
import java.util.logging.Level;
 | 
			
		||||
import java.util.logging.Logger;
 | 
			
		||||
 | 
			
		||||
import envoy.client.event.MessageCreationEvent;
 | 
			
		||||
import envoy.data.Message;
 | 
			
		||||
import envoy.data.Message.MessageStatus;
 | 
			
		||||
import envoy.event.EventBus;
 | 
			
		||||
import envoy.util.EnvoyLog;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Project: <strong>envoy-client</strong><br>
 | 
			
		||||
@@ -20,17 +17,12 @@ import envoy.util.EnvoyLog;
 | 
			
		||||
 */
 | 
			
		||||
public class ReceivedMessageProcessor implements Consumer<Message> {
 | 
			
		||||
 | 
			
		||||
	private static final Logger logger = EnvoyLog.getLogger(ReceivedMessageProcessor.class);
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void accept(Message message) {
 | 
			
		||||
		if (message.getStatus() != MessageStatus.SENT) logger.log(Level.WARNING, "The message has the unexpected status " + message.getStatus());
 | 
			
		||||
		else {
 | 
			
		||||
			// Update status to RECEIVED
 | 
			
		||||
			message.nextStatus();
 | 
			
		||||
		// Update status to RECEIVED
 | 
			
		||||
		if (message.getStatus() == MessageStatus.SENT) message.nextStatus();
 | 
			
		||||
 | 
			
		||||
			// Dispatch event
 | 
			
		||||
			EventBus.getInstance().dispatch(new MessageCreationEvent(message));
 | 
			
		||||
		}
 | 
			
		||||
		// Dispatch event
 | 
			
		||||
		EventBus.getInstance().dispatch(new MessageCreationEvent(message));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -121,12 +121,13 @@ public final class Startup extends Application {
 | 
			
		||||
	@Override
 | 
			
		||||
	public void stop() {
 | 
			
		||||
		try {
 | 
			
		||||
			logger.log(Level.INFO, "Saving local database and settings...");
 | 
			
		||||
			localDB.save(client.isOnline());
 | 
			
		||||
			Settings.getInstance().save();
 | 
			
		||||
 | 
			
		||||
			logger.log(Level.INFO, "Closing connection...");
 | 
			
		||||
			client.close();
 | 
			
		||||
 | 
			
		||||
			logger.log(Level.INFO, "Saving local database and settings...");
 | 
			
		||||
			localDB.save();
 | 
			
		||||
			Settings.getInstance().save();
 | 
			
		||||
			logger.log(Level.INFO, "Envoy was terminated by its user");
 | 
			
		||||
		} catch (final Exception e) {
 | 
			
		||||
			logger.log(Level.SEVERE, "Unable to save local files: ", e);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
package envoy.client.ui.controller;
 | 
			
		||||
 | 
			
		||||
import static envoy.data.Message.MessageStatus.RECEIVED;
 | 
			
		||||
 | 
			
		||||
import java.awt.Toolkit;
 | 
			
		||||
import java.awt.datatransfer.StringSelection;
 | 
			
		||||
import java.io.ByteArrayInputStream;
 | 
			
		||||
@@ -157,7 +159,13 @@ public final class ChatScene implements Restorable {
 | 
			
		||||
		// Listen to received messages
 | 
			
		||||
		eventBus.register(MessageCreationEvent.class, e -> {
 | 
			
		||||
			final var message = e.get();
 | 
			
		||||
			localDB.getChat(message instanceof GroupMessage ? message.getRecipientID() : message.getSenderID()).ifPresent(chat -> {
 | 
			
		||||
 | 
			
		||||
			// The sender of the message is the recipient of the chat
 | 
			
		||||
			// Exceptions: this user is the sender (sync) or group message (group is
 | 
			
		||||
			// recipient)
 | 
			
		||||
			final long recipientID = message instanceof GroupMessage || message.getSenderID() == localDB.getUser().getID() ? message.getRecipientID()
 | 
			
		||||
					: message.getSenderID();
 | 
			
		||||
			localDB.getChat(recipientID).ifPresent(chat -> {
 | 
			
		||||
				chat.insert(message);
 | 
			
		||||
				if (chat.equals(currentChat)) {
 | 
			
		||||
					try {
 | 
			
		||||
@@ -166,8 +174,10 @@ public final class ChatScene implements Restorable {
 | 
			
		||||
						logger.log(Level.WARNING, "Could not read current chat: ", e1);
 | 
			
		||||
					}
 | 
			
		||||
					Platform.runLater(() -> { ListViewRefresh.deepRefresh(messageList); scrollToMessageListEnd(); });
 | 
			
		||||
				} else chat.incrementUnreadAmount();
 | 
			
		||||
				// Moving chat with most recent unreadMessages to the top
 | 
			
		||||
					// TODO: Increment unread counter for group messages with status < RECEIVED
 | 
			
		||||
				} else if (message.getSenderID() != localDB.getUser().getID() && message.getStatus() == RECEIVED) chat.incrementUnreadAmount();
 | 
			
		||||
 | 
			
		||||
				// Move chat with most recent unread messages to the top
 | 
			
		||||
				Platform.runLater(() -> {
 | 
			
		||||
					chatList.getItems().remove(chat);
 | 
			
		||||
					chatList.getItems().add(0, chat);
 | 
			
		||||
@@ -205,12 +215,11 @@ public final class ChatScene implements Restorable {
 | 
			
		||||
			final var contact = e.get();
 | 
			
		||||
			switch (e.getOperationType()) {
 | 
			
		||||
				case ADD:
 | 
			
		||||
					localDB.getUsers().put(contact.getName(), contact);
 | 
			
		||||
					final Chat chat = contact instanceof User ? new Chat(contact) : new GroupChat(client.getSender(), contact);
 | 
			
		||||
					if (contact instanceof User) localDB.getUsers().put(contact.getName(), (User) contact);
 | 
			
		||||
					Chat chat = contact instanceof User ? new Chat(contact) : new GroupChat(client.getSender(), contact);
 | 
			
		||||
					Platform.runLater(() -> chatList.getItems().add(chat));
 | 
			
		||||
					break;
 | 
			
		||||
				case REMOVE:
 | 
			
		||||
					localDB.getUsers().remove(contact.getName());
 | 
			
		||||
					Platform.runLater(() -> chatList.getItems().removeIf(c -> c.getRecipient().equals(contact)));
 | 
			
		||||
					break;
 | 
			
		||||
			}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ package envoy.client.ui.controller;
 | 
			
		||||
 | 
			
		||||
import java.io.FileNotFoundException;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.time.Instant;
 | 
			
		||||
import java.util.concurrent.TimeoutException;
 | 
			
		||||
import java.util.logging.Level;
 | 
			
		||||
import java.util.logging.Logger;
 | 
			
		||||
@@ -109,7 +110,8 @@ public final class LoginScene {
 | 
			
		||||
		userTextField.requestFocus();
 | 
			
		||||
 | 
			
		||||
		// Perform automatic login if configured
 | 
			
		||||
		if (config.hasLoginCredentials()) performHandshake(config.getLoginCredentials());
 | 
			
		||||
		if (config.hasLoginCredentials())
 | 
			
		||||
			performHandshake(new LoginCredentials(config.getUser(), config.getPassword(), false, Startup.VERSION, loadLastSync(config.getUser())));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@FXML
 | 
			
		||||
@@ -123,12 +125,13 @@ public final class LoginScene {
 | 
			
		||||
			new Alert(AlertType.ERROR, "The entered user name is not valid (" + Bounds.CONTACT_NAME_PATTERN + ")").showAndWait();
 | 
			
		||||
			userTextField.clear();
 | 
			
		||||
		} else performHandshake(new LoginCredentials(userTextField.getText(), passwordField.getText(), registration,
 | 
			
		||||
				Startup.VERSION));
 | 
			
		||||
				Startup.VERSION, loadLastSync(userTextField.getText())));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@FXML
 | 
			
		||||
	private void offlineModeButtonPressed() {
 | 
			
		||||
		attemptOfflineMode(new LoginCredentials(userTextField.getText(), passwordField.getText(), false, Startup.VERSION));
 | 
			
		||||
		attemptOfflineMode(new LoginCredentials(userTextField.getText(), passwordField.getText(), false, Startup.VERSION,
 | 
			
		||||
				loadLastSync(userTextField.getText())));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@FXML
 | 
			
		||||
@@ -158,6 +161,18 @@ public final class LoginScene {
 | 
			
		||||
		System.exit(0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private Instant loadLastSync(String identifier) {
 | 
			
		||||
		try {
 | 
			
		||||
			localDB.loadUsers();
 | 
			
		||||
			localDB.setUser(localDB.getUsers().get(identifier));
 | 
			
		||||
			localDB.initializeUserStorage();
 | 
			
		||||
			localDB.loadUserData();
 | 
			
		||||
		} catch (Exception e) {
 | 
			
		||||
			// User storage empty, wrong user name etc. -> default lastSync
 | 
			
		||||
		}
 | 
			
		||||
		return localDB.getLastSync();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void performHandshake(LoginCredentials credentials) {
 | 
			
		||||
		try {
 | 
			
		||||
			client.performHandshake(credentials, cacheMap);
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@ package envoy.client.ui.listcell;
 | 
			
		||||
import java.awt.Toolkit;
 | 
			
		||||
import java.awt.datatransfer.StringSelection;
 | 
			
		||||
import java.io.ByteArrayInputStream;
 | 
			
		||||
import java.time.ZoneId;
 | 
			
		||||
import java.time.format.DateTimeFormatter;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.logging.Level;
 | 
			
		||||
@@ -35,11 +36,12 @@ import envoy.util.EnvoyLog;
 | 
			
		||||
 */
 | 
			
		||||
public class MessageControl extends Label {
 | 
			
		||||
 | 
			
		||||
	private static User								client;
 | 
			
		||||
	private static final DateTimeFormatter			dateFormat		= DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm");
 | 
			
		||||
	private static final Map<MessageStatus, Image>	statusImages	= IconUtil.loadByEnum(MessageStatus.class, 16);
 | 
			
		||||
	private static User client;
 | 
			
		||||
 | 
			
		||||
	private static final Logger logger = EnvoyLog.getLogger(MessageControl.class);
 | 
			
		||||
	private static final DateTimeFormatter			dateFormat		= DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss")
 | 
			
		||||
		.withZone(ZoneId.systemDefault());
 | 
			
		||||
	private static final Map<MessageStatus, Image>	statusImages	= IconUtil.loadByEnum(MessageStatus.class, 16);
 | 
			
		||||
	private static final Logger						logger			= EnvoyLog.getLogger(MessageControl.class);
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 *
 | 
			
		||||
@@ -68,7 +70,8 @@ public class MessageControl extends Label {
 | 
			
		||||
		if (message.hasAttachment()) {
 | 
			
		||||
			switch (message.getAttachment().getType()) {
 | 
			
		||||
				case PICTURE:
 | 
			
		||||
					vbox.getChildren().add(new ImageView(new Image(new ByteArrayInputStream(message.getAttachment().getData()), 256, 256, true, true)));
 | 
			
		||||
					vbox.getChildren()
 | 
			
		||||
						.add(new ImageView(new Image(new ByteArrayInputStream(message.getAttachment().getData()), 256, 256, true, true)));
 | 
			
		||||
					break;
 | 
			
		||||
				case VIDEO:
 | 
			
		||||
					break;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
server=localhost
 | 
			
		||||
port=8080
 | 
			
		||||
localDB=.\\localDB
 | 
			
		||||
localDB=localDB
 | 
			
		||||
consoleLevelBarrier=FINER
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user