Merge branch 'develop' into f/enhanced_UI
This commit is contained in:
commit
c529e8df9e
@ -1,6 +1,6 @@
|
|||||||
# Envoy Client
|
# Envoy Client
|
||||||
|
|
||||||
<a href="https://github.com/informatik-ag-ngl/envoy-client"><img src="https://raw.githubusercontent.com/informatik-ag-ngl/envoy-client/develop/src/main/resources/envoy_logo.png" align="left" width="200" height="200"></a>
|
<a href="https://github.com/informatik-ag-ngl/envoy-client"><img src="https://raw.githubusercontent.com/informatik-ag-ngl/envoy-client/develop/src/main/resources/icons/envoy_logo.png" align="left" width="200" height="200"></a>
|
||||||
|
|
||||||
**Envoy Client** is one of two repositories needed to use the messenger Envoy.<br>
|
**Envoy Client** is one of two repositories needed to use the messenger Envoy.<br>
|
||||||
The other one is <a href="https://github.com/informatik-ag-ngl/envoy-common">**Envoy Common**</a>.
|
The other one is <a href="https://github.com/informatik-ag-ngl/envoy-common">**Envoy Common**</a>.
|
||||||
|
@ -10,8 +10,8 @@ import java.util.logging.Logger;
|
|||||||
import envoy.util.EnvoyLog;
|
import envoy.util.EnvoyLog;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores elements in a queue to process them later.<br>
|
* Stores elements in a queue to process them later.
|
||||||
* <br>
|
* <p>
|
||||||
* Project: <strong>envoy-client</strong><br>
|
* Project: <strong>envoy-client</strong><br>
|
||||||
* File: <strong>Cache.java</strong><br>
|
* File: <strong>Cache.java</strong><br>
|
||||||
* Created: <strong>6 Feb 2020</strong><br>
|
* Created: <strong>6 Feb 2020</strong><br>
|
||||||
@ -40,6 +40,9 @@ public class Cache<T> implements Consumer<T>, Serializable {
|
|||||||
elements.offer(element);
|
elements.offer(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() { return String.format("Cache[elements=" + elements + "]"); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the processor to which cached elements are relayed.
|
* Sets the processor to which cached elements are relayed.
|
||||||
*
|
*
|
||||||
|
@ -66,6 +66,25 @@ public abstract class LocalDB {
|
|||||||
*/
|
*/
|
||||||
public void loadIDGenerator() {}
|
public void loadIDGenerator() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronizes the contact list of the client user with the chat and user
|
||||||
|
* storage.
|
||||||
|
*
|
||||||
|
* @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));
|
||||||
|
users.put(user.getName(), user);
|
||||||
|
|
||||||
|
// Synchronize user status data
|
||||||
|
for (Contact contact : users.values())
|
||||||
|
if (contact instanceof User)
|
||||||
|
getChat(contact.getID()).ifPresent(chat -> { ((User) chat.getRecipient()).setStatus(((User) contact).getStatus()); });
|
||||||
|
|
||||||
|
// Create missing chats
|
||||||
|
user.getContacts().stream().filter(u -> !u.equals(user) && getChat(u.getID()).isEmpty()).map(Chat::new).forEach(chats::add);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return a {@code Map<String, User>} of all users stored locally with their
|
* @return a {@code Map<String, User>} of all users stored locally with their
|
||||||
* user names as keys
|
* user names as keys
|
||||||
@ -73,11 +92,6 @@ public abstract class LocalDB {
|
|||||||
*/
|
*/
|
||||||
public Map<String, Contact> getUsers() { return users; }
|
public Map<String, Contact> getUsers() { return users; }
|
||||||
|
|
||||||
/**
|
|
||||||
* @param users the users to set
|
|
||||||
*/
|
|
||||||
public void setUsers(Map<String, Contact> users) { this.users = users; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return all saved {@link Chat} objects that list the client user as the
|
* @return all saved {@link Chat} objects that list the client user as the
|
||||||
* sender
|
* sender
|
||||||
@ -162,9 +176,7 @@ public abstract class LocalDB {
|
|||||||
* @return an optional containing the chat
|
* @return an optional containing the chat
|
||||||
* @since Envoy Client v0.1-beta
|
* @since Envoy Client v0.1-beta
|
||||||
*/
|
*/
|
||||||
public Optional<Chat> getChat(long recipientID) {
|
public Optional<Chat> getChat(long recipientID) { return chats.stream().filter(c -> c.getRecipient().getID() == recipientID).findAny(); }
|
||||||
return chats.stream().filter(c -> c.getRecipient().getID() == recipientID).findAny();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs a contact name change if the corresponding contact is present.
|
* Performs a contact name change if the corresponding contact is present.
|
||||||
@ -200,13 +212,4 @@ public abstract class LocalDB {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new {@link Chat} for all {@link Contact}s that do not have a chat.
|
|
||||||
*
|
|
||||||
* @since Envoy Client v0.1-beta
|
|
||||||
*/
|
|
||||||
public void createMissingChats() {
|
|
||||||
users.values().stream().filter(u -> !u.equals(user) && getChat(u.getID()).isEmpty()).map(Chat::new).forEach(chats::add);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,6 @@ package envoy.client.net;
|
|||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@ -43,7 +40,6 @@ public class Client implements Closeable {
|
|||||||
|
|
||||||
// Asynchronously initialized during handshake
|
// Asynchronously initialized during handshake
|
||||||
private volatile User sender;
|
private volatile User sender;
|
||||||
private volatile Set<? extends Contact> contacts;
|
|
||||||
private volatile boolean rejected;
|
private volatile boolean rejected;
|
||||||
|
|
||||||
// Configuration, logging and event management
|
// Configuration, logging and event management
|
||||||
@ -73,9 +69,9 @@ public class Client implements Closeable {
|
|||||||
* waiting for the handshake response
|
* waiting for the handshake response
|
||||||
*/
|
*/
|
||||||
public void performHandshake(LoginCredentials credentials, Cache<Message> receivedMessageCache,
|
public void performHandshake(LoginCredentials credentials, Cache<Message> receivedMessageCache,
|
||||||
Cache<MessageStatusChange> receivedMessageStatusChangeCache)
|
Cache<MessageStatusChange> receivedMessageStatusChangeCache) throws TimeoutException, IOException, InterruptedException {
|
||||||
throws TimeoutException, IOException, InterruptedException {
|
|
||||||
if (online) throw new IllegalStateException("Handshake has already been performed successfully");
|
if (online) throw new IllegalStateException("Handshake has already been performed successfully");
|
||||||
|
|
||||||
// Establish TCP connection
|
// Establish TCP connection
|
||||||
logger.log(Level.FINER, String.format("Attempting connection to server %s:%d...", config.getServer(), config.getPort()));
|
logger.log(Level.FINER, String.format("Attempting connection to server %s:%d...", config.getServer(), config.getPort()));
|
||||||
socket = new Socket(config.getServer(), config.getPort());
|
socket = new Socket(config.getServer(), config.getPort());
|
||||||
@ -85,7 +81,7 @@ public class Client implements Closeable {
|
|||||||
receiver = new Receiver(socket.getInputStream());
|
receiver = new Receiver(socket.getInputStream());
|
||||||
|
|
||||||
// Register user creation processor, contact list processor and message cache
|
// Register user creation processor, contact list processor and message cache
|
||||||
receiver.registerProcessor(User.class, sender -> { this.sender = sender; contacts = sender.getContacts(); });
|
receiver.registerProcessor(User.class, sender -> this.sender = sender);
|
||||||
receiver.registerProcessor(Message.class, receivedMessageCache);
|
receiver.registerProcessor(Message.class, receivedMessageCache);
|
||||||
receiver.registerProcessor(MessageStatusChange.class, receivedMessageStatusChangeCache);
|
receiver.registerProcessor(MessageStatusChange.class, receivedMessageStatusChangeCache);
|
||||||
receiver.registerProcessor(HandshakeRejection.class, evt -> { rejected = true; eventBus.dispatch(evt); });
|
receiver.registerProcessor(HandshakeRejection.class, evt -> { rejected = true; eventBus.dispatch(evt); });
|
||||||
@ -142,8 +138,8 @@ public class Client implements Closeable {
|
|||||||
* requested from the server
|
* requested from the server
|
||||||
* @since Envoy Client v0.2-alpha
|
* @since Envoy Client v0.2-alpha
|
||||||
*/
|
*/
|
||||||
public void initReceiver(LocalDB localDB, Cache<Message> receivedMessageCache,
|
public void initReceiver(LocalDB localDB, Cache<Message> receivedMessageCache, Cache<MessageStatusChange> receivedMessageStatusChangeCache)
|
||||||
Cache<MessageStatusChange> receivedMessageStatusChangeCache) throws IOException {
|
throws IOException {
|
||||||
checkOnline();
|
checkOnline();
|
||||||
|
|
||||||
// Process incoming messages
|
// Process incoming messages
|
||||||
@ -182,8 +178,7 @@ public class Client implements Closeable {
|
|||||||
try {
|
try {
|
||||||
sendEvent(evt.get());
|
sendEvent(evt.get());
|
||||||
} catch (final IOException e) {
|
} catch (final IOException e) {
|
||||||
e.printStackTrace();
|
logger.log(Level.WARNING, "An error occurred when trying to send " + evt, e);
|
||||||
logger.log(Level.WARNING, "An error occurred when trying to send Event " + evt, e);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -234,19 +229,6 @@ public class Client implements Closeable {
|
|||||||
writeObject(new IDGeneratorRequest());
|
writeObject(new IDGeneratorRequest());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return a {@code Map<String, User>} of all users on the server with their
|
|
||||||
* user names as keys
|
|
||||||
* @since Envoy Client v0.2-alpha
|
|
||||||
*/
|
|
||||||
public Map<String, Contact> getUsers() {
|
|
||||||
checkOnline();
|
|
||||||
final Map<String, Contact> users = new HashMap<>();
|
|
||||||
contacts.forEach(u -> users.put(u.getName(), u));
|
|
||||||
users.put(sender.getName(), sender);
|
|
||||||
return users;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException { if (online) socket.close(); }
|
public void close() throws IOException { if (online) socket.close(); }
|
||||||
|
|
||||||
@ -259,7 +241,7 @@ public class Client implements Closeable {
|
|||||||
private void checkOnline() { if (!online) throw new IllegalStateException("Client is not online"); }
|
private void checkOnline() { if (!online) throw new IllegalStateException("Client is not online"); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the sender object that represents this client.
|
* @return the {@link User} as which this client is logged in
|
||||||
* @since Envoy Client v0.1-alpha
|
* @since Envoy Client v0.1-alpha
|
||||||
*/
|
*/
|
||||||
public User getSender() { return sender; }
|
public User getSender() { return sender; }
|
||||||
@ -282,16 +264,4 @@ public class Client implements Closeable {
|
|||||||
* @since Envoy Client v0.2-alpha
|
* @since Envoy Client v0.2-alpha
|
||||||
*/
|
*/
|
||||||
public boolean isOnline() { return online; }
|
public boolean isOnline() { return online; }
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the contacts of this {@link Client}
|
|
||||||
* @since Envoy Client v0.3-alpha
|
|
||||||
*/
|
|
||||||
public Set<? extends Contact> getContacts() { return contacts; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param contacts the contacts to set
|
|
||||||
* @since Envoy Client v0.3-alpha
|
|
||||||
*/
|
|
||||||
public void setContacts(Set<? extends Contact> contacts) { this.contacts = contacts; }
|
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,6 @@ public class Receiver extends Thread {
|
|||||||
// Connection probably closed by client.
|
// Connection probably closed by client.
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
logger.log(Level.SEVERE, "Error on receiver thread", e);
|
logger.log(Level.SEVERE, "Error on receiver thread", e);
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,9 +47,6 @@ public class WriteProxy {
|
|||||||
try {
|
try {
|
||||||
logger.log(Level.FINER, "Sending cached " + msg);
|
logger.log(Level.FINER, "Sending cached " + msg);
|
||||||
client.sendMessage(msg);
|
client.sendMessage(msg);
|
||||||
|
|
||||||
// Update message state to SENT in localDB
|
|
||||||
localDB.getMessage(msg.getID()).ifPresent(Message::nextStatus);
|
|
||||||
} catch (final IOException e) {
|
} catch (final IOException e) {
|
||||||
logger.log(Level.SEVERE, "Could not send cached message: ", e);
|
logger.log(Level.SEVERE, "Could not send cached message: ", e);
|
||||||
}
|
}
|
||||||
|
@ -176,12 +176,12 @@ public final class ChatScene {
|
|||||||
private void userListClicked() {
|
private void userListClicked() {
|
||||||
final Contact user = userList.getSelectionModel().getSelectedItem();
|
final Contact user = userList.getSelectionModel().getSelectedItem();
|
||||||
if (user != null && (currentChat == null || !user.equals(currentChat.getRecipient()))) {
|
if (user != null && (currentChat == null || !user.equals(currentChat.getRecipient()))) {
|
||||||
|
logger.log(Level.FINEST, "Loading chat with " + user);
|
||||||
|
|
||||||
// LEON: JFC <===> JAVA FRIED CHICKEN <=/=> Java Foundation Classes
|
// LEON: JFC <===> JAVA FRIED CHICKEN <=/=> Java Foundation Classes
|
||||||
|
|
||||||
// Load the chat or create a new one and add it to the LocalDB
|
// Load the chat
|
||||||
currentChat = localDB.getChat(user.getID())
|
currentChat = localDB.getChat(user.getID()).get();
|
||||||
.orElseGet(() -> { final var chat = new Chat(user); localDB.getChats().add(chat); return chat; });
|
|
||||||
|
|
||||||
messageList.setItems(FXCollections.observableList(currentChat.getMessages()));
|
messageList.setItems(FXCollections.observableList(currentChat.getMessages()));
|
||||||
deleteContactMenuItem.setText("Delete " + user.getName());
|
deleteContactMenuItem.setText("Delete " + user.getName());
|
||||||
|
@ -11,9 +11,7 @@ import javafx.fxml.FXML;
|
|||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
import javafx.scene.control.Alert.AlertType;
|
import javafx.scene.control.Alert.AlertType;
|
||||||
|
|
||||||
import envoy.client.data.Cache;
|
import envoy.client.data.*;
|
||||||
import envoy.client.data.ClientConfig;
|
|
||||||
import envoy.client.data.LocalDB;
|
|
||||||
import envoy.client.net.Client;
|
import envoy.client.net.Client;
|
||||||
import envoy.client.ui.SceneContext;
|
import envoy.client.ui.SceneContext;
|
||||||
import envoy.client.ui.Startup;
|
import envoy.client.ui.Startup;
|
||||||
@ -183,7 +181,6 @@ public final class LoginScene {
|
|||||||
} catch (final FileNotFoundException e) {
|
} catch (final FileNotFoundException e) {
|
||||||
// The local database file has not yet been created, probably first login
|
// The local database file has not yet been created, probably first login
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
e.printStackTrace();
|
|
||||||
new Alert(AlertType.ERROR, "Error while loading local database: " + e + "\nChats will not be stored locally.").showAndWait();
|
new Alert(AlertType.ERROR, "Error while loading local database: " + e + "\nChats will not be stored locally.").showAndWait();
|
||||||
logger.log(Level.WARNING, "Could not load local database: ", e);
|
logger.log(Level.WARNING, "Could not load local database: ", e);
|
||||||
}
|
}
|
||||||
@ -191,15 +188,18 @@ public final class LoginScene {
|
|||||||
// Initialize write proxy
|
// Initialize write proxy
|
||||||
final var writeProxy = client.createWriteProxy(localDB);
|
final var writeProxy = client.createWriteProxy(localDB);
|
||||||
|
|
||||||
if (client.isOnline()) {
|
localDB.synchronize();
|
||||||
|
|
||||||
// Save all users to the local database and flush cache
|
if (client.isOnline()) {
|
||||||
localDB.setUsers(client.getUsers());
|
|
||||||
localDB.createMissingChats();
|
|
||||||
writeProxy.flushCache();
|
writeProxy.flushCache();
|
||||||
} else
|
} else
|
||||||
// Set all contacts to offline mode
|
// Set all contacts to offline mode
|
||||||
localDB.getUsers().values().stream().filter(User.class::isInstance).map(User.class::cast).forEach(u -> u.setStatus(UserStatus.OFFLINE));
|
localDB.getChats()
|
||||||
|
.stream()
|
||||||
|
.map(Chat::getRecipient)
|
||||||
|
.filter(User.class::isInstance)
|
||||||
|
.map(User.class::cast)
|
||||||
|
.forEach(u -> u.setStatus(UserStatus.OFFLINE));
|
||||||
|
|
||||||
// Load ChatScene
|
// Load ChatScene
|
||||||
sceneContext.pop();
|
sceneContext.pop();
|
||||||
|
Reference in New Issue
Block a user