Merge remote-tracking branch 'origin/develop' into
b/sanitized-issue-proposals Conflicts: client/src/main/java/envoy/client/ui/settings/BugReportPane.java client/src/main/java/envoy/client/util/IssueUtil.java
This commit is contained in:
commit
844a256af0
@ -9,16 +9,17 @@ import envoy.client.ui.Startup;
|
||||
* <p>
|
||||
* To allow Maven shading, the main method has to be separated from the
|
||||
* {@link Startup} class which extends {@link Application}.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>Main.java</strong><br>
|
||||
* Created: <strong>05.07.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public final class Main {
|
||||
|
||||
/**
|
||||
* A funny debug switch put in by {@code delvh} to enable easy debugging.
|
||||
*
|
||||
* @since Envoy Client v0.2-beta
|
||||
*/
|
||||
private static final boolean debug = false;
|
||||
|
||||
/**
|
||||
|
@ -1,20 +1,14 @@
|
||||
package envoy.client.data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.logging.*;
|
||||
|
||||
import envoy.util.EnvoyLog;
|
||||
|
||||
/**
|
||||
* Stores elements in a queue to process them later.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>Cache.java</strong><br>
|
||||
* Created: <strong>6 Feb 2020</strong><br>
|
||||
*
|
||||
* @param <T> the type of cached elements
|
||||
* @author Kai S. K. Engelbart
|
||||
|
@ -1,16 +1,11 @@
|
||||
package envoy.client.data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Stores a heterogeneous map of {@link Cache} objects with different type
|
||||
* parameters.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>CacheMap.java</strong><br>
|
||||
* Created: <strong>09.07.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
|
@ -1,25 +1,18 @@
|
||||
package envoy.client.data;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import javafx.collections.*;
|
||||
|
||||
import envoy.client.net.WriteProxy;
|
||||
import envoy.data.Contact;
|
||||
import envoy.data.Message;
|
||||
import envoy.data.*;
|
||||
import envoy.data.Message.MessageStatus;
|
||||
import envoy.data.User;
|
||||
import envoy.event.MessageStatusChange;
|
||||
|
||||
/**
|
||||
* Represents a chat between two {@link User}s
|
||||
* as a list of {@link Message} objects.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>Chat.java</strong><br>
|
||||
* Created: <strong>19 Oct 2019</strong><br>
|
||||
*
|
||||
* @author Maximilian Käfer
|
||||
* @author Leon Hofmeister
|
||||
@ -29,7 +22,8 @@ import envoy.event.MessageStatusChange;
|
||||
public class Chat implements Serializable {
|
||||
|
||||
protected final Contact recipient;
|
||||
protected final List<Message> messages = new ArrayList<>();
|
||||
|
||||
protected transient ObservableList<Message> messages = FXCollections.observableArrayList();
|
||||
|
||||
protected int unreadAmount;
|
||||
|
||||
@ -38,7 +32,7 @@ public class Chat implements Serializable {
|
||||
*/
|
||||
protected transient long lastWritingEvent;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final long serialVersionUID = 2L;
|
||||
|
||||
/**
|
||||
* Provides the list of messages that the recipient receives.
|
||||
@ -50,8 +44,18 @@ public class Chat implements Serializable {
|
||||
*/
|
||||
public Chat(Contact recipient) { this.recipient = recipient; }
|
||||
|
||||
private void readObject(ObjectInputStream stream) throws ClassNotFoundException, IOException {
|
||||
stream.defaultReadObject();
|
||||
messages = FXCollections.observableList((List<Message>) stream.readObject());
|
||||
}
|
||||
|
||||
private void writeObject(ObjectOutputStream stream) throws IOException {
|
||||
stream.defaultWriteObject();
|
||||
stream.writeObject(new ArrayList<>(messages));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() { return String.format("Chat[recipient=%s,messages=%d]", recipient, messages.size()); }
|
||||
public String toString() { return String.format("%s[recipient=%s,messages=%d]", getClass().getSimpleName(), recipient, messages.size()); }
|
||||
|
||||
/**
|
||||
* Generates a hash code based on the recipient.
|
||||
@ -81,11 +85,9 @@ public class Chat implements Serializable {
|
||||
*
|
||||
* @param writeProxy the write proxy instance used to notify the server about
|
||||
* the message status changes
|
||||
* @throws IOException if a {@link MessageStatusChange} could not be
|
||||
* delivered to the server
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public void read(WriteProxy writeProxy) throws IOException {
|
||||
public void read(WriteProxy writeProxy) {
|
||||
for (int i = messages.size() - 1; i >= 0; --i) {
|
||||
final Message m = messages.get(i);
|
||||
if (m.getSenderID() == recipient.getID()) if (m.getStatus() == MessageStatus.READ) break;
|
||||
@ -136,7 +138,7 @@ public class Chat implements Serializable {
|
||||
* @return all messages in the current chat
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public List<Message> getMessages() { return messages; }
|
||||
public ObservableList<Message> getMessages() { return messages; }
|
||||
|
||||
/**
|
||||
* @return the recipient of a message
|
||||
|
@ -7,10 +7,6 @@ import envoy.data.Config;
|
||||
/**
|
||||
* Implements a configuration specific to the Envoy Client with default values
|
||||
* and convenience methods.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>ClientConfig.java</strong><br>
|
||||
* Created: <strong>01.03.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
|
@ -2,16 +2,11 @@ package envoy.client.data;
|
||||
|
||||
import javafx.stage.Stage;
|
||||
|
||||
import envoy.client.net.Client;
|
||||
import envoy.client.net.WriteProxy;
|
||||
import envoy.client.net.*;
|
||||
import envoy.client.ui.SceneContext;
|
||||
|
||||
/**
|
||||
* Provides access to commonly used objects.
|
||||
* <p>
|
||||
* Project: <strong>client</strong><br>
|
||||
* File: <strong>Context.java</strong><br>
|
||||
* Created: <strong>01.09.2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Client v0.2-beta
|
||||
|
@ -1,22 +1,15 @@
|
||||
package envoy.client.data;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
|
||||
import envoy.client.net.WriteProxy;
|
||||
import envoy.data.Contact;
|
||||
import envoy.data.GroupMessage;
|
||||
import envoy.data.*;
|
||||
import envoy.data.Message.MessageStatus;
|
||||
import envoy.data.User;
|
||||
import envoy.event.GroupMessageStatusChange;
|
||||
|
||||
/**
|
||||
* Represents a chat between a user and a group
|
||||
* as a list of messages.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>GroupChat.java</strong><br>
|
||||
* Created: <strong>05.07.2020</strong><br>
|
||||
*
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy Client v0.1-beta
|
||||
@ -38,7 +31,7 @@ public final class GroupChat extends Chat {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(WriteProxy writeProxy) throws IOException {
|
||||
public void read(WriteProxy writeProxy) {
|
||||
for (int i = messages.size() - 1; i >= 0; --i) {
|
||||
final GroupMessage gmsg = (GroupMessage) messages.get(i);
|
||||
if (gmsg.getSenderID() != sender.getID()) if (gmsg.getMemberStatuses().get(sender.getID()) == MessageStatus.READ) break;
|
||||
|
@ -5,10 +5,13 @@ import java.nio.channels.*;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.*;
|
||||
|
||||
import javafx.collections.*;
|
||||
|
||||
import envoy.client.event.EnvoyCloseEvent;
|
||||
import envoy.data.*;
|
||||
import envoy.data.Message.MessageStatus;
|
||||
import envoy.event.*;
|
||||
import envoy.exception.EnvoyException;
|
||||
import envoy.util.*;
|
||||
@ -22,10 +25,6 @@ import dev.kske.eventbus.EventListener;
|
||||
* For message ID generation a {@link IDGenerator} is stored as well.
|
||||
* <p>
|
||||
* The managed objects are stored inside a folder in the local file system.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>LocalDB.java</strong><br>
|
||||
* Created: <strong>3 Feb 2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.3-alpha
|
||||
@ -35,7 +34,7 @@ public final class LocalDB implements EventListener {
|
||||
// Data
|
||||
private User user;
|
||||
private Map<String, User> users = Collections.synchronizedMap(new HashMap<>());
|
||||
private List<Chat> chats = Collections.synchronizedList(new ArrayList<>());
|
||||
private ObservableList<Chat> chats = FXCollections.observableArrayList();
|
||||
private IDGenerator idGenerator;
|
||||
private CacheMap cacheMap = new CacheMap();
|
||||
private String authToken;
|
||||
@ -49,6 +48,8 @@ public final class LocalDB implements EventListener {
|
||||
|
||||
private final File dbDir, idGeneratorFile, lastLoginFile, usersFile;
|
||||
|
||||
private static final Logger logger = EnvoyLog.getLogger(LocalDB.class);
|
||||
|
||||
/**
|
||||
* Constructs an empty local database.
|
||||
*
|
||||
@ -130,7 +131,7 @@ public final class LocalDB implements EventListener {
|
||||
if (user == null) throw new IllegalStateException("Client user is null, cannot initialize user storage");
|
||||
userFile = new File(dbDir, user.getID() + ".db");
|
||||
try (var in = new ObjectInputStream(new FileInputStream(userFile))) {
|
||||
chats = (List<Chat>) in.readObject();
|
||||
chats = FXCollections.observableList((List<Chat>) in.readObject());
|
||||
cacheMap = (CacheMap) in.readObject();
|
||||
lastSync = (Instant) in.readObject();
|
||||
} finally {
|
||||
@ -190,8 +191,8 @@ public final class LocalDB implements EventListener {
|
||||
SerializationUtils.write(usersFile, users);
|
||||
|
||||
// Save user data and last sync time stamp
|
||||
if (user != null)
|
||||
SerializationUtils.write(userFile, chats, cacheMap, Context.getInstance().getClient().isOnline() ? Instant.now() : lastSync);
|
||||
if (user != null) SerializationUtils
|
||||
.write(userFile, new ArrayList<>(chats), cacheMap, Context.getInstance().getClient().isOnline() ? Instant.now() : lastSync);
|
||||
|
||||
// Save last login information
|
||||
if (authToken != null) SerializationUtils.write(lastLoginFile, user, authToken);
|
||||
@ -203,6 +204,37 @@ public final class LocalDB implements EventListener {
|
||||
}
|
||||
}
|
||||
|
||||
@Event(priority = 150)
|
||||
private void onMessage(Message msg) { if (msg.getStatus() == MessageStatus.SENT) msg.nextStatus(); }
|
||||
|
||||
@Event(priority = 150)
|
||||
private void onGroupMessage(GroupMessage msg) {
|
||||
// TODO: Cancel event once EventBus is updated
|
||||
if (msg.getStatus() == MessageStatus.WAITING || msg.getStatus() == MessageStatus.READ)
|
||||
logger.warning("The groupMessage has the unexpected status " + msg.getStatus());
|
||||
}
|
||||
|
||||
@Event(priority = 150)
|
||||
private void onMessageStatusChange(MessageStatusChange evt) { getMessage(evt.getID()).ifPresent(msg -> msg.setStatus(evt.get())); }
|
||||
|
||||
@Event(priority = 150)
|
||||
private void onGroupMessageStatusChange(GroupMessageStatusChange evt) {
|
||||
this.<GroupMessage>getMessage(evt.getID()).ifPresent(msg -> msg.getMemberStatuses().replace(evt.getMemberID(), evt.get()));
|
||||
}
|
||||
|
||||
@Event(priority = 150)
|
||||
private void onUserStatusChange(UserStatusChange evt) {
|
||||
this.getChat(evt.getID()).map(Chat::getRecipient).map(User.class::cast).ifPresent(u -> u.setStatus(evt.get()));
|
||||
}
|
||||
|
||||
@Event(priority = 150)
|
||||
private void onGroupResize(GroupResize evt) { getChat(evt.getGroupID()).map(Chat::getRecipient).map(Group.class::cast).ifPresent(evt::apply); }
|
||||
|
||||
@Event(priority = 150)
|
||||
private void onNameChange(NameChange evt) {
|
||||
chats.stream().map(Chat::getRecipient).filter(c -> c.getID() == evt.getID()).findAny().ifPresent(c -> c.setName(evt.get()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a new authentication token.
|
||||
*
|
||||
@ -219,17 +251,32 @@ public final class LocalDB implements EventListener {
|
||||
*/
|
||||
public Map<String, User> getUsers() { return users; }
|
||||
|
||||
/**
|
||||
* Searches for a message by ID.
|
||||
*
|
||||
* @param id the ID of the message to search for
|
||||
* @return an optional containing the message
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public <T extends Message> Optional<T> getMessage(long id) {
|
||||
return (Optional<T>) 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(); }
|
||||
|
||||
/**
|
||||
* @return all saved {@link Chat} objects that list the client user as the
|
||||
* sender
|
||||
* @since Envoy Client v0.1-alpha
|
||||
**/
|
||||
public List<Chat> getChats() { return chats; }
|
||||
|
||||
/**
|
||||
* @param chats the chats to set
|
||||
*/
|
||||
public void setChats(List<Chat> chats) { this.chats = chats; }
|
||||
public ObservableList<Chat> getChats() { return chats; }
|
||||
|
||||
/**
|
||||
* @return the {@link User} who initialized the local database
|
||||
@ -253,6 +300,7 @@ public final class LocalDB implements EventListener {
|
||||
* @param idGenerator the message ID generator to set
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
@Event(priority = 150)
|
||||
public void setIDGenerator(IDGenerator idGenerator) { this.idGenerator = idGenerator; }
|
||||
|
||||
/**
|
||||
@ -278,59 +326,4 @@ public final class LocalDB implements EventListener {
|
||||
* @since Envoy Client v0.2-beta
|
||||
*/
|
||||
public String getAuthToken() { return authToken; }
|
||||
|
||||
/**
|
||||
* Searches for a message by ID.
|
||||
*
|
||||
* @param id the ID of the message to search for
|
||||
* @return an optional containing the message
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
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(); }
|
||||
|
||||
/**
|
||||
* Performs a contact name change if the corresponding contact is present.
|
||||
*
|
||||
* @param event the {@link NameChange} to process
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public void replaceContactName(NameChange event) {
|
||||
chats.stream().map(Chat::getRecipient).filter(c -> c.getID() == event.getID()).findAny().ifPresent(c -> c.setName(event.get()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a group resize operation if the corresponding group is present.
|
||||
*
|
||||
* @param event the {@link GroupResize} to process
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public void updateGroup(GroupResize event) {
|
||||
chats.stream()
|
||||
.map(Chat::getRecipient)
|
||||
.filter(Group.class::isInstance)
|
||||
.filter(g -> g.getID() == event.getGroupID() && g.getID() != user.getID())
|
||||
.map(Group.class::cast)
|
||||
.findAny()
|
||||
.ifPresent(group -> {
|
||||
switch (event.getOperation()) {
|
||||
case ADD:
|
||||
group.getContacts().add(event.get());
|
||||
break;
|
||||
case REMOVE:
|
||||
group.getContacts().remove(event.get());
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -15,10 +15,6 @@ import dev.kske.eventbus.EventListener;
|
||||
* Manages all application settings, which are different objects that can be
|
||||
* changed during runtime and serialized them by using either the file system or
|
||||
* the {@link Preferences} API.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>Settings.java</strong><br>
|
||||
* Created: <strong>11 Nov 2019</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @author Maximilian Käfer
|
||||
|
@ -8,10 +8,6 @@ import javax.swing.JComponent;
|
||||
/**
|
||||
* Encapsulates a persistent value that is directly or indirectly mutable by the
|
||||
* user.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>SettingsItem.java</strong><br>
|
||||
* Created: <strong>23.12.2019</strong><br>
|
||||
*
|
||||
* @param <T> the type of this {@link SettingsItem}'s value
|
||||
* @author Kai S. K. Engelbart
|
||||
|
@ -6,10 +6,6 @@ import envoy.exception.EnvoyException;
|
||||
|
||||
/**
|
||||
* Plays back audio from a byte array.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>AudioPlayer.java</strong><br>
|
||||
* Created: <strong>05.07.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
|
@ -1,8 +1,7 @@
|
||||
package envoy.client.data.audio;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.*;
|
||||
|
||||
import javax.sound.sampled.*;
|
||||
|
||||
@ -10,10 +9,6 @@ import envoy.exception.EnvoyException;
|
||||
|
||||
/**
|
||||
* Records audio and exports it as a byte array.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>AudioRecorder.java</strong><br>
|
||||
* Created: <strong>02.07.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
|
@ -1,9 +1,5 @@
|
||||
/**
|
||||
* Contains classes related to recording and playing back audio clips.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>package-info.java</strong><br>
|
||||
* Created: <strong>05.07.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
|
@ -5,10 +5,6 @@ import java.util.function.Supplier;
|
||||
/**
|
||||
* This interface defines an action that should be performed when a system
|
||||
* command gets called.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>OnCall.java</strong><br>
|
||||
* Created: <strong>23.07.2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Client v0.2-beta
|
||||
|
@ -1,10 +1,7 @@
|
||||
package envoy.client.data.commands;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.*;
|
||||
import java.util.function.*;
|
||||
|
||||
/**
|
||||
* This class is the base class of all {@code SystemCommands} and contains an
|
||||
@ -16,10 +13,6 @@ import java.util.function.Supplier;
|
||||
* function. This approach has one limitation:<br>
|
||||
* <b>Order matters!</b> Changing the order of arguments will likely result in
|
||||
* unexpected behavior.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>SystemCommand.java</strong><br>
|
||||
* Created: <strong>16.07.2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Client v0.2-beta
|
||||
|
@ -1,15 +1,10 @@
|
||||
package envoy.client.data.commands;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* This class acts as a builder for {@link SystemCommand}s.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>SystemCommandBuilder.java</strong><br>
|
||||
* Created: <strong>23.07.2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Client v0.2-beta
|
||||
|
@ -10,10 +10,6 @@ import envoy.util.EnvoyLog;
|
||||
|
||||
/**
|
||||
* This class stores all {@link SystemCommand}s used.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>SystemCommandsMap.java</strong><br>
|
||||
* Created: <strong>17.07.2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Client v0.2-beta
|
||||
|
@ -1,10 +1,6 @@
|
||||
/**
|
||||
* This package contains all classes that can be used as system commands.<br>
|
||||
* Every system command can be called using a specific syntax:"/<command>"
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>package-info.java</strong><br>
|
||||
* Created: <strong>16.07.2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Client v0.2-beta
|
||||
|
@ -3,12 +3,8 @@ package envoy.client.event;
|
||||
import envoy.event.Event.Valueless;
|
||||
|
||||
/**
|
||||
* This event serves the purpose to trigger the tab change to tab 0 in
|
||||
* This event serves the purpose of triggering the tab change to tab 0 in
|
||||
* {@link envoy.client.ui.controller.ChatScene}.
|
||||
* <p>
|
||||
* Project: <strong>client</strong><br>
|
||||
* File: <strong>BackEvent.java</strong><br>
|
||||
* Created: <strong>23.08.2020</strong><br>
|
||||
*
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy Client v0.2-beta
|
||||
|
@ -3,9 +3,9 @@ package envoy.client.event;
|
||||
import envoy.event.Event.Valueless;
|
||||
|
||||
/**
|
||||
* This event will be sent once Envoy is <strong>really</strong> closed.
|
||||
* Its purpose is to forcefully stop other threads peacefully so that the VM can
|
||||
* shutdown too.
|
||||
* This event notifies various Envoy components of the application being about
|
||||
* to shut down. This allows the graceful closing of connections, persisting
|
||||
* local data etc.
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Client v0.2-beta
|
||||
|
@ -1,22 +0,0 @@
|
||||
package envoy.client.event;
|
||||
|
||||
import envoy.event.Event;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>SendEvent.java</strong><br>
|
||||
* Created: <strong>11.02.2020</strong><br>
|
||||
*
|
||||
* @author: Maximilian Käfer
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public final class SendEvent extends Event<Event<?>> {
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
/**
|
||||
* @param value the event to send to the server
|
||||
*/
|
||||
public SendEvent(Event<?> value) { super(value); }
|
||||
|
||||
}
|
@ -3,9 +3,7 @@ package envoy.client.event;
|
||||
import envoy.event.Event;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>ThemeChangeEvent.java</strong><br>
|
||||
* Created: <strong>15 Dec 2019</strong><br>
|
||||
* Notifies UI components of a theme change.
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.2-alpha
|
||||
|
@ -6,22 +6,17 @@ import java.util.concurrent.TimeoutException;
|
||||
import java.util.logging.*;
|
||||
|
||||
import envoy.client.data.*;
|
||||
import envoy.client.event.*;
|
||||
import envoy.client.event.EnvoyCloseEvent;
|
||||
import envoy.data.*;
|
||||
import envoy.event.*;
|
||||
import envoy.event.Event;
|
||||
import envoy.event.contact.*;
|
||||
import envoy.util.*;
|
||||
|
||||
import dev.kske.eventbus.*;
|
||||
import dev.kske.eventbus.Event;
|
||||
|
||||
/**
|
||||
* Establishes a connection to the server, performs a handshake and delivers
|
||||
* certain objects to the server.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>Client.java</strong><br>
|
||||
* Created: <strong>28 Sep 2019</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @author Maximilian Käfer
|
||||
@ -79,8 +74,6 @@ public final class Client implements EventListener, Closeable {
|
||||
// authentication token
|
||||
receiver.registerProcessor(User.class, sender -> this.sender = sender);
|
||||
receiver.registerProcessors(cacheMap.getMap());
|
||||
receiver.registerProcessor(HandshakeRejection.class, evt -> { rejected = true; eventBus.dispatch(evt); });
|
||||
receiver.registerProcessor(NewAuthToken.class, eventBus::dispatch);
|
||||
|
||||
rejected = false;
|
||||
|
||||
@ -128,113 +121,64 @@ public final class Client implements EventListener, Closeable {
|
||||
// Remove all processors as they are only used during the handshake
|
||||
receiver.removeAllProcessors();
|
||||
|
||||
// Process incoming messages
|
||||
final var receivedMessageProcessor = new ReceivedMessageProcessor();
|
||||
final var receivedGroupMessageProcessor = new ReceivedGroupMessageProcessor();
|
||||
final var messageStatusChangeProcessor = new MessageStatusChangeProcessor();
|
||||
final var groupMessageStatusChangeProcessor = new GroupMessageStatusChangeProcessor();
|
||||
|
||||
receiver.registerProcessor(GroupMessage.class, receivedGroupMessageProcessor);
|
||||
receiver.registerProcessor(Message.class, receivedMessageProcessor);
|
||||
receiver.registerProcessor(MessageStatusChange.class, messageStatusChangeProcessor);
|
||||
receiver.registerProcessor(GroupMessageStatusChange.class, groupMessageStatusChangeProcessor);
|
||||
|
||||
// Relay cached messages and message status changes
|
||||
cacheMap.get(Message.class).setProcessor(receivedMessageProcessor);
|
||||
cacheMap.get(GroupMessage.class).setProcessor(receivedGroupMessageProcessor);
|
||||
cacheMap.get(MessageStatusChange.class).setProcessor(messageStatusChangeProcessor);
|
||||
cacheMap.get(GroupMessageStatusChange.class).setProcessor(groupMessageStatusChangeProcessor);
|
||||
|
||||
// Process user status changes
|
||||
receiver.registerProcessor(UserStatusChange.class, eventBus::dispatch);
|
||||
|
||||
// Process message ID generation
|
||||
receiver.registerProcessor(IDGenerator.class, localDB::setIDGenerator);
|
||||
|
||||
// Process name changes
|
||||
receiver.registerProcessor(NameChange.class, evt -> { localDB.replaceContactName(evt); eventBus.dispatch(evt); });
|
||||
|
||||
// Process contact searches
|
||||
receiver.registerProcessor(UserSearchResult.class, eventBus::dispatch);
|
||||
|
||||
// Process contact operations
|
||||
receiver.registerProcessor(ContactOperation.class, eventBus::dispatch);
|
||||
|
||||
// Process group size changes
|
||||
receiver.registerProcessor(GroupResize.class, evt -> { localDB.updateGroup(evt); eventBus.dispatch(evt); });
|
||||
|
||||
// Process IsTyping events
|
||||
receiver.registerProcessor(IsTyping.class, eventBus::dispatch);
|
||||
|
||||
// Process PasswordChangeResults
|
||||
receiver.registerProcessor(PasswordChangeResult.class, eventBus::dispatch);
|
||||
|
||||
// Process ProfilePicChanges
|
||||
receiver.registerProcessor(ProfilePicChange.class, eventBus::dispatch);
|
||||
|
||||
// Process requests to not send any more attachments as they will not be shown
|
||||
// to other users
|
||||
receiver.registerProcessor(NoAttachments.class, eventBus::dispatch);
|
||||
|
||||
// Process group creation results - they might have been disabled on the server
|
||||
receiver.registerProcessor(GroupCreationResult.class, eventBus::dispatch);
|
||||
cacheMap.get(Message.class).setProcessor(eventBus::dispatch);
|
||||
cacheMap.get(GroupMessage.class).setProcessor(eventBus::dispatch);
|
||||
cacheMap.get(MessageStatusChange.class).setProcessor(eventBus::dispatch);
|
||||
cacheMap.get(GroupMessageStatusChange.class).setProcessor(eventBus::dispatch);
|
||||
|
||||
// Request a generator if none is present or the existing one is consumed
|
||||
if (!localDB.hasIDGenerator() || !localDB.getIDGenerator().hasNext()) requestIdGenerator();
|
||||
if (!localDB.hasIDGenerator() || !localDB.getIDGenerator().hasNext()) requestIDGenerator();
|
||||
|
||||
// Relay caches
|
||||
cacheMap.getMap().values().forEach(Cache::relay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an object to the server.
|
||||
*
|
||||
* @param obj the object to send
|
||||
* @throws IllegalStateException if the client is not online
|
||||
* @throws RuntimeException if the object serialization failed
|
||||
* @since Envoy Client v0.2-beta
|
||||
*/
|
||||
public void send(Serializable obj) throws IllegalStateException, RuntimeException {
|
||||
checkOnline();
|
||||
logger.log(Level.FINE, "Sending " + obj);
|
||||
try {
|
||||
SerializationUtils.writeBytesWithLength(obj, socket.getOutputStream());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to the server. The message's status will be incremented once
|
||||
* it was delivered successfully.
|
||||
*
|
||||
* @param message the message to send
|
||||
* @throws IOException if the message does not reach the server
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public void sendMessage(Message message) throws IOException {
|
||||
writeObject(message);
|
||||
public void sendMessage(Message message) {
|
||||
send(message);
|
||||
message.nextStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an event to the server.
|
||||
*
|
||||
* @param evt the event to send
|
||||
* @throws IOException if the event did not reach the server
|
||||
*/
|
||||
public void sendEvent(Event<?> evt) throws IOException { if (online) writeObject(evt); }
|
||||
|
||||
/**
|
||||
* Requests a new {@link IDGenerator} from the server.
|
||||
*
|
||||
* @throws IOException if the request does not reach the server
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public void requestIdGenerator() throws IOException {
|
||||
public void requestIDGenerator() {
|
||||
logger.log(Level.INFO, "Requesting new id generator...");
|
||||
writeObject(new IDGeneratorRequest());
|
||||
send(new IDGeneratorRequest());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the value of a send event to the server.
|
||||
*
|
||||
* @param evt the send event to extract the value from
|
||||
* @since Envoy Client v0.2-beta
|
||||
*/
|
||||
@dev.kske.eventbus.Event
|
||||
private void onSendEvent(SendEvent evt) {
|
||||
try {
|
||||
sendEvent(evt.get());
|
||||
} catch (final IOException e) {
|
||||
logger.log(Level.WARNING, "An error occurred when trying to send " + evt, e);
|
||||
}
|
||||
}
|
||||
@Event(eventType = HandshakeRejection.class, priority = 1000)
|
||||
private void onHandshakeRejection() { rejected = true; }
|
||||
|
||||
@Override
|
||||
@dev.kske.eventbus.Event(eventType = EnvoyCloseEvent.class, priority = 800)
|
||||
@Event(eventType = EnvoyCloseEvent.class, priority = 800)
|
||||
public void close() {
|
||||
if (online) {
|
||||
logger.log(Level.INFO, "Closing connection...");
|
||||
@ -244,13 +188,13 @@ public final class Client implements EventListener, Closeable {
|
||||
}
|
||||
}
|
||||
|
||||
private void writeObject(Object obj) throws IOException {
|
||||
checkOnline();
|
||||
logger.log(Level.FINE, "Sending " + obj);
|
||||
SerializationUtils.writeBytesWithLength(obj, socket.getOutputStream());
|
||||
}
|
||||
|
||||
private void checkOnline() { if (!online) throw new IllegalStateException("Client is not online"); }
|
||||
/**
|
||||
* Ensured that the client is online.
|
||||
*
|
||||
* @throws IllegalStateException if the client is not online
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
private void checkOnline() throws IllegalStateException { if (!online) throw new IllegalStateException("Client is not online"); }
|
||||
|
||||
/**
|
||||
* @return the {@link User} as which this client is logged in
|
||||
|
@ -1,29 +0,0 @@
|
||||
package envoy.client.net;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import envoy.data.Message.MessageStatus;
|
||||
import envoy.event.GroupMessageStatusChange;
|
||||
import envoy.util.EnvoyLog;
|
||||
|
||||
import dev.kske.eventbus.EventBus;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>GroupMessageStatusChangePocessor.java</strong><br>
|
||||
* Created: <strong>03.07.2020</strong><br>
|
||||
*
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public final class GroupMessageStatusChangeProcessor implements Consumer<GroupMessageStatusChange> {
|
||||
|
||||
private static final Logger logger = EnvoyLog.getLogger(GroupMessageStatusChangeProcessor.class);
|
||||
|
||||
@Override
|
||||
public void accept(GroupMessageStatusChange evt) {
|
||||
if (evt.get().ordinal() < MessageStatus.RECEIVED.ordinal()) logger.warning("Received invalid group message status change " + evt);
|
||||
else EventBus.getInstance().dispatch(evt);
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
package envoy.client.net;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import envoy.data.Message.MessageStatus;
|
||||
import envoy.event.MessageStatusChange;
|
||||
import envoy.util.EnvoyLog;
|
||||
|
||||
import dev.kske.eventbus.EventBus;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>MessageStatusChangeProcessor.java</strong><br>
|
||||
* Created: <strong>4 Feb 2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public final class MessageStatusChangeProcessor implements Consumer<MessageStatusChange> {
|
||||
|
||||
private static final Logger logger = EnvoyLog.getLogger(MessageStatusChangeProcessor.class);
|
||||
|
||||
/**
|
||||
* Dispatches a {@link MessageStatusChange} if the status is
|
||||
* {@code RECEIVED} or {@code READ}.
|
||||
*
|
||||
* @param evt the status change event
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
@Override
|
||||
public void accept(MessageStatusChange evt) {
|
||||
if (evt.get().ordinal() < MessageStatus.RECEIVED.ordinal()) logger.warning("Received invalid message status change " + evt);
|
||||
else EventBus.getInstance().dispatch(evt);
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
package envoy.client.net;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import envoy.data.GroupMessage;
|
||||
import envoy.data.Message.MessageStatus;
|
||||
import envoy.util.EnvoyLog;
|
||||
|
||||
import dev.kske.eventbus.EventBus;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>ReceivedGroupMessageProcessor.java</strong><br>
|
||||
* Created: <strong>13.06.2020</strong><br>
|
||||
*
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public final class ReceivedGroupMessageProcessor implements Consumer<GroupMessage> {
|
||||
|
||||
private static final Logger logger = EnvoyLog.getLogger(ReceivedGroupMessageProcessor.class);
|
||||
|
||||
@Override
|
||||
public void accept(GroupMessage groupMessage) {
|
||||
if (groupMessage.getStatus() == MessageStatus.WAITING || groupMessage.getStatus() == MessageStatus.READ)
|
||||
logger.warning("The groupMessage has the unexpected status " + groupMessage.getStatus());
|
||||
|
||||
// Dispatch event
|
||||
EventBus.getInstance().dispatch(groupMessage);
|
||||
}
|
||||
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
package envoy.client.net;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import envoy.data.Message;
|
||||
import envoy.data.Message.MessageStatus;
|
||||
|
||||
import dev.kske.eventbus.EventBus;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>ReceivedMessageProcessor.java</strong><br>
|
||||
* Created: <strong>31.12.2019</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public final class ReceivedMessageProcessor implements Consumer<Message> {
|
||||
|
||||
@Override
|
||||
public void accept(Message message) {
|
||||
// Update status to RECEIVED
|
||||
if (message.getStatus() == MessageStatus.SENT) message.nextStatus();
|
||||
|
||||
// Dispatch message
|
||||
EventBus.getInstance().dispatch(message);
|
||||
}
|
||||
}
|
@ -8,13 +8,11 @@ import java.util.logging.*;
|
||||
|
||||
import envoy.util.*;
|
||||
|
||||
import dev.kske.eventbus.*;
|
||||
|
||||
/**
|
||||
* Receives objects from the server and passes them to processor objects based
|
||||
* on their class.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>Receiver.java</strong><br>
|
||||
* Created: <strong>30.12.2019</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.3-alpha
|
||||
@ -26,6 +24,7 @@ public final class Receiver extends Thread {
|
||||
private final InputStream in;
|
||||
private final Map<Class<?>, Consumer<?>> processors = new HashMap<>();
|
||||
|
||||
private static final EventBus eventBus = EventBus.getInstance();
|
||||
private static final Logger logger = EnvoyLog.getLogger(Receiver.class);
|
||||
|
||||
/**
|
||||
@ -81,9 +80,14 @@ public final class Receiver extends Thread {
|
||||
// Get appropriate processor
|
||||
@SuppressWarnings("rawtypes")
|
||||
final Consumer processor = processors.get(obj.getClass());
|
||||
if (processor == null)
|
||||
logger.log(Level.WARNING, String.format("The received object has the %s for which no processor is defined.", obj.getClass()));
|
||||
else processor.accept(obj);
|
||||
|
||||
// Dispatch to the processor if present
|
||||
if (processor != null) processor.accept(obj);
|
||||
// Dispatch to the event bus if the object is an event without a processor
|
||||
else if (obj instanceof IEvent) eventBus.dispatch((IEvent) obj);
|
||||
// Notify if no processor could be located
|
||||
else logger.log(Level.WARNING,
|
||||
String.format("The received object has the %s for which no processor is defined.", obj.getClass()));
|
||||
}
|
||||
} catch (final SocketException | EOFException e) {
|
||||
// Connection probably closed by client.
|
||||
|
@ -1,11 +1,8 @@
|
||||
package envoy.client.net;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.logging.*;
|
||||
|
||||
import envoy.client.data.Cache;
|
||||
import envoy.client.data.LocalDB;
|
||||
import envoy.client.data.*;
|
||||
import envoy.data.Message;
|
||||
import envoy.event.MessageStatusChange;
|
||||
import envoy.util.EnvoyLog;
|
||||
@ -14,10 +11,6 @@ import envoy.util.EnvoyLog;
|
||||
* Implements methods to send {@link Message}s and
|
||||
* {@link MessageStatusChange}s to the server or cache them inside a
|
||||
* {@link LocalDB} depending on the online status.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>WriteProxy.java</strong><br>
|
||||
* Created: <strong>6 Feb 2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.3-alpha
|
||||
@ -44,20 +37,12 @@ public final class WriteProxy {
|
||||
|
||||
// Initialize cache processors for messages and message status change events
|
||||
localDB.getCacheMap().get(Message.class).setProcessor(msg -> {
|
||||
try {
|
||||
logger.log(Level.FINER, "Sending cached " + msg);
|
||||
client.sendMessage(msg);
|
||||
} catch (final IOException e) {
|
||||
logger.log(Level.SEVERE, "Could not send cached message: ", e);
|
||||
}
|
||||
});
|
||||
localDB.getCacheMap().get(MessageStatusChange.class).setProcessor(evt -> {
|
||||
logger.log(Level.FINER, "Sending cached " + evt);
|
||||
try {
|
||||
client.sendEvent(evt);
|
||||
} catch (final IOException e) {
|
||||
logger.log(Level.SEVERE, "Could not send cached message status change event: ", e);
|
||||
}
|
||||
client.send(evt);
|
||||
});
|
||||
}
|
||||
|
||||
@ -74,10 +59,9 @@ public final class WriteProxy {
|
||||
* inside the local database.
|
||||
*
|
||||
* @param message the message to send
|
||||
* @throws IOException if the message could not be sent
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public void writeMessage(Message message) throws IOException {
|
||||
public void writeMessage(Message message) {
|
||||
if (client.isOnline()) client.sendMessage(message);
|
||||
else localDB.getCacheMap().getApplicable(Message.class).accept(message);
|
||||
}
|
||||
@ -87,11 +71,10 @@ public final class WriteProxy {
|
||||
* event is cached inside the local database.
|
||||
*
|
||||
* @param evt the event to send
|
||||
* @throws IOException if the event could not be sent
|
||||
* @since Envoy Client v0.3-alpha
|
||||
*/
|
||||
public void writeMessageStatusChange(MessageStatusChange evt) throws IOException {
|
||||
if (client.isOnline()) client.sendEvent(evt);
|
||||
public void writeMessageStatusChange(MessageStatusChange evt) {
|
||||
if (client.isOnline()) client.send(evt);
|
||||
else localDB.getCacheMap().getApplicable(MessageStatusChange.class).accept(evt);
|
||||
}
|
||||
}
|
||||
|
@ -1,169 +0,0 @@
|
||||
package envoy.client.ui;
|
||||
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.Background;
|
||||
import javafx.scene.layout.ColumnConstraints;
|
||||
import javafx.scene.layout.GridPane;
|
||||
|
||||
/**
|
||||
* This class offers a text field that is automatically equipped with a clear
|
||||
* button.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>ClearableTextField.java</strong><br>
|
||||
* Created: <strong>25.06.2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public final class ClearableTextField extends GridPane {
|
||||
|
||||
private final TextField textField;
|
||||
|
||||
private final Button clearButton;
|
||||
|
||||
/**
|
||||
* Constructs a new {@code ClearableTextField} with no initial text and icon
|
||||
* size 16.
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public ClearableTextField() { this("", 16); }
|
||||
|
||||
/**
|
||||
* Constructs a new {@code ClearableTextField} with initial text and a
|
||||
* predetermined icon size.
|
||||
*
|
||||
* @param text the text that should be displayed by default
|
||||
* @param size the size of the icon
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public ClearableTextField(String text, int size) {
|
||||
// initializing the textField and the button
|
||||
textField = new TextField(text);
|
||||
clearButton = new Button("", new ImageView(IconUtil.loadIconThemeSensitive("clear_button", size)));
|
||||
clearButton.setOnAction(e -> textField.clear());
|
||||
clearButton.setFocusTraversable(false);
|
||||
clearButton.getStyleClass().clear();
|
||||
clearButton.setBackground(Background.EMPTY);
|
||||
// Adding the two elements to the GridPane
|
||||
add(textField, 0, 0, 2, 1);
|
||||
add(clearButton, 1, 0, 1, 1);
|
||||
// Setting the percent - widths of the two columns.
|
||||
// Used to locate the button on the right.
|
||||
final var columnConstraints = new ColumnConstraints();
|
||||
columnConstraints.setPercentWidth(90);
|
||||
getColumnConstraints().add(columnConstraints);
|
||||
final var columnConstraints2 = new ColumnConstraints();
|
||||
columnConstraints2.setPercentWidth(10);
|
||||
getColumnConstraints().add(columnConstraints2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the underlying {@code textField}
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public TextField getTextField() { return textField; }
|
||||
|
||||
/**
|
||||
* This method offers the freedom to perform custom actions when the
|
||||
* {@code clearButton} has been pressed.
|
||||
* <p>
|
||||
* The default is
|
||||
* <b><code> e -> {clearableTextField.getTextField().clear();}</code></b>
|
||||
*
|
||||
* @param onClearButtonAction the action that should be performed
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public void setClearButtonListener(EventHandler<ActionEvent> onClearButtonAction) { clearButton.setOnAction(onClearButtonAction); }
|
||||
|
||||
/**
|
||||
* @return the current property of the prompt text
|
||||
* @see javafx.scene.control.TextInputControl#promptTextProperty()
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public StringProperty promptTextProperty() { return textField.promptTextProperty(); }
|
||||
|
||||
/**
|
||||
* @return the current prompt text
|
||||
* @see javafx.scene.control.TextInputControl#getPromptText()
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public String getPromptText() { return textField.getPromptText(); }
|
||||
|
||||
/**
|
||||
* @param value the prompt text to display
|
||||
* @see javafx.scene.control.TextInputControl#setPromptText(java.lang.String)
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public void setPromptText(String value) { textField.setPromptText(value); }
|
||||
|
||||
/**
|
||||
* @return the current property of the tooltip
|
||||
* @see javafx.scene.control.Control#tooltipProperty()
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public ObjectProperty<Tooltip> tooltipProperty() { return textField.tooltipProperty(); }
|
||||
|
||||
/**
|
||||
* @param value the new tooltip
|
||||
* @see javafx.scene.control.Control#setTooltip(javafx.scene.control.Tooltip)
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public void setTooltip(Tooltip value) { textField.setTooltip(value); }
|
||||
|
||||
/**
|
||||
* @return the current tooltip
|
||||
* @see javafx.scene.control.Control#getTooltip()
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public Tooltip getTooltip() { return textField.getTooltip(); }
|
||||
|
||||
/**
|
||||
* @return the current property of the context menu
|
||||
* @see javafx.scene.control.Control#contextMenuProperty()
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public ObjectProperty<ContextMenu> contextMenuProperty() { return textField.contextMenuProperty(); }
|
||||
|
||||
/**
|
||||
* @param value the new context menu
|
||||
* @see javafx.scene.control.Control#setContextMenu(javafx.scene.control.ContextMenu)
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public void setContextMenu(ContextMenu value) { textField.setContextMenu(value); }
|
||||
|
||||
/**
|
||||
* @return the current context menu
|
||||
* @see javafx.scene.control.Control#getContextMenu()
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public ContextMenu getContextMenu() { return textField.getContextMenu(); }
|
||||
|
||||
/**
|
||||
* @param value whether this ClearableTextField should be editable
|
||||
* @see javafx.scene.control.TextInputControl#setEditable(boolean)
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public void setEditable(boolean value) { textField.setEditable(value); }
|
||||
|
||||
/**
|
||||
* @return the current property whether this ClearableTextField is editable
|
||||
* @see javafx.scene.control.TextInputControl#editableProperty()
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public BooleanProperty editableProperty() { return textField.editableProperty(); }
|
||||
|
||||
/**
|
||||
* @return whether this {@code ClearableTextField} is editable
|
||||
* @see javafx.scene.control.TextInputControl#isEditable()
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public boolean isEditable() { return textField.isEditable(); }
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
package envoy.client.ui;
|
||||
|
||||
import javafx.scene.control.ListCell;
|
||||
import javafx.scene.control.ListView;
|
||||
|
||||
/**
|
||||
* This is a utility class that provides access to a refreshing mechanism for
|
||||
* elements that were added without notifying the underlying {@link ListView}.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>ListViewRefresh.java</strong><br>
|
||||
* Created: <strong>16.07.2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public final class ListViewRefresh {
|
||||
|
||||
private ListViewRefresh() {}
|
||||
|
||||
/**
|
||||
* Deeply refreshes a {@code listview}, meaning it recomputes every single of
|
||||
* its {@link ListCell}s.
|
||||
* <p>
|
||||
* While it does work, it is <b>not the most efficient algorithm</b> possible.
|
||||
*
|
||||
* @param toRefresh the listView to refresh
|
||||
* @param <T> the type of its {@code listcells}
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public static <T> void deepRefresh(ListView<T> toRefresh) {
|
||||
final var items = toRefresh.getItems();
|
||||
toRefresh.setItems(null);
|
||||
toRefresh.setItems(items);
|
||||
}
|
||||
}
|
@ -3,10 +3,6 @@ package envoy.client.ui;
|
||||
/**
|
||||
* This interface defines an action that should be performed when a scene gets
|
||||
* restored from the scene stack in {@link SceneContext}.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>Restorable.java</strong><br>
|
||||
* Created: <strong>03.07.2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Client v0.1-beta
|
||||
|
@ -23,10 +23,6 @@ import dev.kske.eventbus.*;
|
||||
* <p>
|
||||
* When a scene is loaded, the style sheet for the current theme is applied to
|
||||
* it.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>SceneContext.java</strong><br>
|
||||
* Created: <strong>06.06.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
|
@ -15,6 +15,7 @@ import envoy.client.event.EnvoyCloseEvent;
|
||||
import envoy.client.net.Client;
|
||||
import envoy.client.ui.SceneContext.SceneInfo;
|
||||
import envoy.client.ui.controller.LoginScene;
|
||||
import envoy.client.util.IconUtil;
|
||||
import envoy.data.*;
|
||||
import envoy.data.User.UserStatus;
|
||||
import envoy.event.*;
|
||||
@ -25,10 +26,6 @@ import dev.kske.eventbus.EventBus;
|
||||
|
||||
/**
|
||||
* Handles application startup and shutdown.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>Startup.java</strong><br>
|
||||
* Created: <strong>26.03.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @author Maximilian Käfer
|
||||
|
@ -6,16 +6,13 @@ import java.awt.TrayIcon.MessageType;
|
||||
import javafx.application.Platform;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
import envoy.client.util.IconUtil;
|
||||
import envoy.data.Message;
|
||||
|
||||
import dev.kske.eventbus.*;
|
||||
import dev.kske.eventbus.Event;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>StatusTrayIcon.java</strong><br>
|
||||
* Created: <strong>3 Dec 2019</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.2-alpha
|
||||
*/
|
||||
@ -32,7 +29,7 @@ public final class StatusTrayIcon implements EventListener {
|
||||
* A received {@link Message} is only displayed as a system tray notification if
|
||||
* this variable is set to {@code true}.
|
||||
*/
|
||||
private boolean displayMessages = false;
|
||||
private boolean displayMessages;
|
||||
|
||||
/**
|
||||
* @return {@code true} if the status tray icon is supported on this platform
|
||||
@ -90,10 +87,10 @@ public final class StatusTrayIcon implements EventListener {
|
||||
public void hide() { SystemTray.getSystemTray().remove(trayIcon); }
|
||||
|
||||
@Event
|
||||
private void onMessage(Message evt) {
|
||||
private void onMessage(Message message) {
|
||||
if (displayMessages) trayIcon.displayMessage(
|
||||
evt.hasAttachment() ? "New " + evt.getAttachment().getType().toString().toLowerCase() + " message received" : "New message received",
|
||||
evt.getText(),
|
||||
message.hasAttachment() ? "New " + message.getAttachment().getType().toString().toLowerCase() + " message received" : "New message received",
|
||||
message.getText(),
|
||||
MessageType.INFO);
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,9 @@
|
||||
package envoy.client.ui;
|
||||
package envoy.client.ui.control;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.logging.*;
|
||||
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.control.Alert.AlertType;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.layout.HBox;
|
||||
|
||||
import envoy.client.data.audio.AudioPlayer;
|
||||
@ -14,10 +12,6 @@ import envoy.util.EnvoyLog;
|
||||
|
||||
/**
|
||||
* Enables the play back of audio clips through a button.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>AudioControl.java</strong><br>
|
||||
* Created: <strong>05.07.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
@ -1,23 +1,17 @@
|
||||
package envoy.client.ui.listcell;
|
||||
package envoy.client.ui.control;
|
||||
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.geometry.*;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.image.*;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.scene.shape.Rectangle;
|
||||
|
||||
import envoy.client.data.Chat;
|
||||
import envoy.client.ui.IconUtil;
|
||||
import envoy.data.Group;
|
||||
import envoy.client.data.*;
|
||||
import envoy.client.util.IconUtil;
|
||||
|
||||
/**
|
||||
* Displays a chat using a contact control for the recipient and a label for the
|
||||
* unread message count.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>ContactControl.java</strong><br>
|
||||
* Created: <strong>01.07.2020</strong><br>
|
||||
*
|
||||
* @see ContactControl
|
||||
* @author Leon Hofmeister
|
||||
@ -25,6 +19,9 @@ import envoy.data.Group;
|
||||
*/
|
||||
public final class ChatControl extends HBox {
|
||||
|
||||
private static final Image userIcon = IconUtil.loadIconThemeSensitive("user_icon", 32),
|
||||
groupIcon = IconUtil.loadIconThemeSensitive("group_icon", 32);
|
||||
|
||||
/**
|
||||
* @param chat the chat to display
|
||||
* @since Envoy Client v0.1-beta
|
||||
@ -32,10 +29,9 @@ public final class ChatControl extends HBox {
|
||||
public ChatControl(Chat chat) {
|
||||
setAlignment(Pos.CENTER_LEFT);
|
||||
setPadding(new Insets(0, 0, 3, 0));
|
||||
// profile pic
|
||||
ImageView contactProfilePic;
|
||||
if (chat.getRecipient() instanceof Group) contactProfilePic = new ImageView(IconUtil.loadIconThemeSensitive("group_icon", 32));
|
||||
else contactProfilePic = new ImageView(IconUtil.loadIconThemeSensitive("user_icon", 32));
|
||||
|
||||
// Profile picture
|
||||
ImageView contactProfilePic = new ImageView(chat instanceof GroupChat ? groupIcon : userIcon);
|
||||
final var clip = new Rectangle();
|
||||
clip.setWidth(32);
|
||||
clip.setHeight(32);
|
||||
@ -43,14 +39,17 @@ public final class ChatControl extends HBox {
|
||||
clip.setArcWidth(32);
|
||||
contactProfilePic.setClip(clip);
|
||||
getChildren().add(contactProfilePic);
|
||||
// spacing
|
||||
|
||||
// Spacing
|
||||
final var leftSpacing = new Region();
|
||||
leftSpacing.setPrefSize(8, 0);
|
||||
leftSpacing.setMinSize(8, 0);
|
||||
leftSpacing.setMaxSize(8, 0);
|
||||
getChildren().add(leftSpacing);
|
||||
|
||||
// Contact control
|
||||
getChildren().add(new ContactControl(chat.getRecipient()));
|
||||
|
||||
// Unread messages
|
||||
if (chat.getUnreadAmount() != 0) {
|
||||
final var spacing = new Region();
|
||||
@ -58,12 +57,12 @@ public final class ChatControl extends HBox {
|
||||
getChildren().add(spacing);
|
||||
final var unreadMessagesLabel = new Label(Integer.toString(chat.getUnreadAmount()));
|
||||
unreadMessagesLabel.setMinSize(15, 15);
|
||||
final var vBox2 = new VBox();
|
||||
vBox2.setAlignment(Pos.CENTER_RIGHT);
|
||||
final var vbox = new VBox();
|
||||
vbox.setAlignment(Pos.CENTER_RIGHT);
|
||||
unreadMessagesLabel.setAlignment(Pos.CENTER);
|
||||
unreadMessagesLabel.getStyleClass().add("unread-messages-amount");
|
||||
vBox2.getChildren().add(unreadMessagesLabel);
|
||||
getChildren().add(vBox2);
|
||||
vbox.getChildren().add(unreadMessagesLabel);
|
||||
getChildren().add(vbox);
|
||||
}
|
||||
getStyleClass().add("list-element");
|
||||
}
|
@ -1,19 +1,14 @@
|
||||
package envoy.client.ui.listcell;
|
||||
package envoy.client.ui.control;
|
||||
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
import envoy.data.Contact;
|
||||
import envoy.data.User;
|
||||
import envoy.data.*;
|
||||
|
||||
/**
|
||||
* Displays information about a contact in two rows. The first row contains the
|
||||
* name. The second row contains the online status (user) or the member count
|
||||
* (group).
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>ContactControl.java</strong><br>
|
||||
* Created: <strong>13.07.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.2-beta
|
@ -1,4 +1,4 @@
|
||||
package envoy.client.ui.listcell;
|
||||
package envoy.client.ui.control;
|
||||
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
@ -6,37 +6,23 @@ import java.io.*;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.logging.*;
|
||||
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.ContextMenu;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.MenuItem;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.geometry.*;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.image.*;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.stage.FileChooser;
|
||||
|
||||
import envoy.client.data.Context;
|
||||
import envoy.client.data.LocalDB;
|
||||
import envoy.client.data.Settings;
|
||||
import envoy.client.ui.AudioControl;
|
||||
import envoy.client.ui.IconUtil;
|
||||
import envoy.client.ui.SceneContext;
|
||||
import envoy.data.GroupMessage;
|
||||
import envoy.data.Message;
|
||||
import envoy.client.data.*;
|
||||
import envoy.client.ui.*;
|
||||
import envoy.client.util.IconUtil;
|
||||
import envoy.data.*;
|
||||
import envoy.data.Message.MessageStatus;
|
||||
import envoy.data.User;
|
||||
import envoy.util.EnvoyLog;
|
||||
|
||||
/**
|
||||
* This class formats a single {@link Message} into a UI component.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>MessageControl.java</strong><br>
|
||||
* Created: <strong>01.07.2020</strong><br>
|
||||
* This class transforms a single {@link Message} into a UI component.
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @author Maximilian Käfer
|
||||
@ -95,6 +81,7 @@ public final class MessageControl extends Label {
|
||||
contextMenu.getItems().addAll(copyMenuItem, deleteMenuItem, forwardMenuItem, quoteMenuItem, infoMenuItem);
|
||||
|
||||
// Handling message attachment display
|
||||
// TODO: Add missing attachment types
|
||||
if (message.hasAttachment()) {
|
||||
switch (message.getAttachment().getType()) {
|
||||
case PICTURE:
|
@ -1,15 +1,10 @@
|
||||
package envoy.client.ui.custom;
|
||||
package envoy.client.ui.control;
|
||||
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.image.*;
|
||||
import javafx.scene.shape.Rectangle;
|
||||
|
||||
/**
|
||||
* Provides a set of convenience constructors for images that are displayed as profile pictures.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>ProfilePicImageView.java</strong><br>
|
||||
* Created: <strong>30.07.2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Client v0.2-beta
|
@ -1,4 +1,4 @@
|
||||
package envoy.client.ui.custom;
|
||||
package envoy.client.ui.control;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@ -21,10 +21,6 @@ import javafx.scene.input.Clipboard;
|
||||
* <li>clear</li>
|
||||
* <li>Select all</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Project: <strong>client</strong><br>
|
||||
* File: <strong>TextInputContextMenu.java</strong><br>
|
||||
* Created: <strong>20.09.2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Client v0.2-beta
|
@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Defines custom UI controls.
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @author Leon Hofmeister
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy Client v0.2-beta
|
||||
*/
|
||||
package envoy.client.ui.control;
|
@ -11,7 +11,7 @@ import java.util.logging.*;
|
||||
|
||||
import javafx.animation.RotateTransition;
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.*;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.transformation.FilteredList;
|
||||
import javafx.fxml.*;
|
||||
import javafx.scene.control.*;
|
||||
@ -30,9 +30,9 @@ import envoy.client.data.commands.*;
|
||||
import envoy.client.event.*;
|
||||
import envoy.client.net.*;
|
||||
import envoy.client.ui.*;
|
||||
import envoy.client.ui.custom.TextInputContextMenu;
|
||||
import envoy.client.ui.control.*;
|
||||
import envoy.client.ui.listcell.*;
|
||||
import envoy.client.util.ReflectionUtil;
|
||||
import envoy.client.util.*;
|
||||
import envoy.data.*;
|
||||
import envoy.data.Attachment.AttachmentType;
|
||||
import envoy.data.Message.MessageStatus;
|
||||
@ -45,9 +45,7 @@ import dev.kske.eventbus.*;
|
||||
import dev.kske.eventbus.Event;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>ChatSceneController.java</strong><br>
|
||||
* Created: <strong>26.03.2020</strong><br>
|
||||
* Controller for the chat scene.
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
@ -169,7 +167,8 @@ public final class ChatScene implements EventListener, Restorable {
|
||||
messageList.setCellFactory(MessageListCell::new);
|
||||
chatList.setCellFactory(new ListCellFactory<>(ChatControl::new));
|
||||
|
||||
// JavaFX provides an internal way of populating the context menu of a textarea.
|
||||
// JavaFX provides an internal way of populating the context menu of a text
|
||||
// area.
|
||||
// We, however, need additional functionality.
|
||||
messageTextArea.setContextMenu(new TextInputContextMenu(messageTextArea, e -> checkKeyCombination(null)));
|
||||
|
||||
@ -188,7 +187,7 @@ public final class ChatScene implements EventListener, Restorable {
|
||||
clip.setArcWidth(43);
|
||||
clientProfilePic.setClip(clip);
|
||||
|
||||
chatList.setItems(chats = new FilteredList<>(FXCollections.observableList(localDB.getChats())));
|
||||
chatList.setItems(chats = new FilteredList<>(localDB.getChats()));
|
||||
contactLabel.setText(localDB.getUser().getName());
|
||||
|
||||
initializeSystemCommandsMap();
|
||||
@ -203,8 +202,8 @@ public final class ChatScene implements EventListener, Restorable {
|
||||
Tooltip.uninstall(contactSpecificOnlineOperations, onlyIfOnlineTooltip);
|
||||
contactSearchTab.setContent(new FXMLLoader().load(getClass().getResourceAsStream("/fxml/ContactSearchTab.fxml")));
|
||||
groupCreationTab.setContent(new FXMLLoader().load(getClass().getResourceAsStream("/fxml/GroupCreationTab.fxml")));
|
||||
} catch (final IOException e2) {
|
||||
logger.log(Level.SEVERE, "An error occurred when attempting to load tabs: ", e2);
|
||||
} catch (final IOException e) {
|
||||
logger.log(Level.SEVERE, "An error occurred when attempting to load tabs: ", e);
|
||||
}
|
||||
else {
|
||||
Tooltip.install(contactSpecificOnlineOperations, onlyIfOnlineTooltip);
|
||||
@ -230,12 +229,8 @@ public final class ChatScene implements EventListener, Restorable {
|
||||
|
||||
// Read current chat or increment unread amount
|
||||
if (chat.equals(currentChat)) {
|
||||
try {
|
||||
currentChat.read(writeProxy);
|
||||
} catch (final IOException e) {
|
||||
logger.log(Level.WARNING, "Could not read current chat: ", e);
|
||||
}
|
||||
Platform.runLater(() -> { ListViewRefresh.deepRefresh(messageList); scrollToMessageListEnd(); });
|
||||
Platform.runLater(this::scrollToMessageListEnd);
|
||||
} else if (!ownMessage && message.getStatus() != MessageStatus.READ) chat.incrementUnreadAmount();
|
||||
|
||||
// Move chat with most recent unread messages to the top
|
||||
@ -250,33 +245,16 @@ public final class ChatScene implements EventListener, Restorable {
|
||||
|
||||
@Event
|
||||
private void onMessageStatusChange(MessageStatusChange evt) {
|
||||
localDB.getMessage(evt.getID()).ifPresent(message -> {
|
||||
message.setStatus(evt.get());
|
||||
|
||||
// Update UI if in current chat and the current user was the sender of the
|
||||
// message
|
||||
if (currentChat != null && message.getSenderID() == client.getSender().getID()) Platform.runLater(messageList::refresh);
|
||||
});
|
||||
if (currentChat != null) localDB.getMessage(evt.getID())
|
||||
.filter(msg -> msg.getSenderID() == client.getSender().getID())
|
||||
.ifPresent(msg -> Platform.runLater(messageList::refresh));
|
||||
}
|
||||
|
||||
@Event
|
||||
private void onGroupMessageStatusChange(GroupMessageStatusChange evt) {
|
||||
localDB.getMessage(evt.getID()).ifPresent(groupMessage -> {
|
||||
((GroupMessage) groupMessage).getMemberStatuses().replace(evt.getMemberID(), evt.get());
|
||||
|
||||
// Update UI if in current chat
|
||||
if (currentChat != null && groupMessage.getRecipientID() == currentChat.getRecipient().getID()) Platform.runLater(messageList::refresh);
|
||||
});
|
||||
}
|
||||
|
||||
@Event
|
||||
private void onUserStatusChange(UserStatusChange evt) {
|
||||
chats.getSource()
|
||||
.stream()
|
||||
.filter(c -> c.getRecipient().getID() == evt.getID())
|
||||
.findAny()
|
||||
.map(Chat::getRecipient)
|
||||
.ifPresent(u -> { ((User) u).setStatus(evt.get()); Platform.runLater(() -> ListViewRefresh.deepRefresh(chatList)); });
|
||||
}
|
||||
@Event(eventType = UserStatusChange.class)
|
||||
private void onUserStatusChange() { Platform.runLater(chatList::refresh); }
|
||||
|
||||
@Event
|
||||
private void onContactOperation(ContactOperation operation) {
|
||||
@ -320,6 +298,7 @@ public final class ChatScene implements EventListener, Restorable {
|
||||
clientProfilePic.setImage(IconUtil.loadIconThemeSensitive("user_icon", 43));
|
||||
chatList.setCellFactory(new ListCellFactory<>(ChatControl::new));
|
||||
messageList.setCellFactory(MessageListCell::new);
|
||||
// TODO: cache image
|
||||
if (currentChat.getRecipient() instanceof User) recipientProfilePic.setImage(IconUtil.loadIconThemeSensitive("user_icon", 43));
|
||||
else recipientProfilePic.setImage(IconUtil.loadIconThemeSensitive("group_icon", 43));
|
||||
}
|
||||
@ -360,18 +339,14 @@ public final class ChatScene implements EventListener, Restorable {
|
||||
// Load the chat
|
||||
currentChat = localDB.getChat(user.getID()).get();
|
||||
|
||||
messageList.setItems(FXCollections.observableList(currentChat.getMessages()));
|
||||
messageList.setItems(currentChat.getMessages());
|
||||
final var scrollIndex = messageList.getItems().size() - currentChat.getUnreadAmount();
|
||||
messageList.scrollTo(scrollIndex);
|
||||
logger.log(Level.FINEST, "Loading chat with " + user + " at index " + scrollIndex);
|
||||
deleteContactMenuItem.setText("Delete " + user.getName());
|
||||
|
||||
// Read the current chat
|
||||
try {
|
||||
currentChat.read(writeProxy);
|
||||
} catch (final IOException e) {
|
||||
logger.log(Level.WARNING, "Could not read current chat.", e);
|
||||
}
|
||||
|
||||
// Discard the pending attachment
|
||||
if (recorder.isRecording()) {
|
||||
@ -555,8 +530,8 @@ public final class ChatScene implements EventListener, Restorable {
|
||||
|
||||
// Sending an IsTyping event if none has been sent for
|
||||
// IsTyping#millisecondsActive
|
||||
if (currentChat.getLastWritingEvent() + IsTyping.millisecondsActive <= System.currentTimeMillis()) {
|
||||
eventBus.dispatch(new SendEvent(new IsTyping(getChatID(), currentChat.getRecipient().getID())));
|
||||
if (client.isOnline() && currentChat.getLastWritingEvent() + IsTyping.millisecondsActive <= System.currentTimeMillis()) {
|
||||
client.send(new IsTyping(getChatID(), currentChat.getRecipient().getID()));
|
||||
currentChat.lastWritingEventWasNow();
|
||||
}
|
||||
|
||||
@ -665,7 +640,7 @@ public final class ChatScene implements EventListener, Restorable {
|
||||
return;
|
||||
}
|
||||
final var text = messageTextArea.getText().strip();
|
||||
if (!messageTextAreaCommands.executeIfAnyPresent(text)) try {
|
||||
if (!messageTextAreaCommands.executeIfAnyPresent(text)) {
|
||||
// Creating the message and its metadata
|
||||
final var builder = new MessageBuilder(localDB.getUser().getID(), currentChat.getRecipient().getID(), localDB.getIDGenerator())
|
||||
.setText(text);
|
||||
@ -692,15 +667,10 @@ public final class ChatScene implements EventListener, Restorable {
|
||||
localDB.getChats().remove(currentChat);
|
||||
localDB.getChats().add(0, currentChat);
|
||||
});
|
||||
ListViewRefresh.deepRefresh(messageList);
|
||||
scrollToMessageListEnd();
|
||||
|
||||
// Request a new ID generator if all IDs were used
|
||||
if (!localDB.getIDGenerator().hasNext() && client.isOnline()) client.requestIdGenerator();
|
||||
|
||||
} catch (final IOException e) {
|
||||
logger.log(Level.SEVERE, "Error while sending message: ", e);
|
||||
new Alert(AlertType.ERROR, "An error occured while sending the message!").showAndWait();
|
||||
if (!localDB.getIDGenerator().hasNext() && client.isOnline()) client.requestIDGenerator();
|
||||
}
|
||||
|
||||
// Clear text field and disable post button
|
||||
|
@ -7,8 +7,11 @@ import javafx.fxml.FXML;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.control.Alert.AlertType;
|
||||
|
||||
import envoy.client.event.*;
|
||||
import envoy.client.ui.listcell.*;
|
||||
import envoy.client.data.Context;
|
||||
import envoy.client.event.BackEvent;
|
||||
import envoy.client.net.Client;
|
||||
import envoy.client.ui.control.ContactControl;
|
||||
import envoy.client.ui.listcell.ListCellFactory;
|
||||
import envoy.data.User;
|
||||
import envoy.event.ElementOperation;
|
||||
import envoy.event.contact.*;
|
||||
@ -25,10 +28,6 @@ import dev.kske.eventbus.*;
|
||||
* <p>
|
||||
* To create a group, a button is available that loads the
|
||||
* {@link GroupCreationTab}.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>ContactSearchScene.java</strong><br>
|
||||
* Created: <strong>07.06.2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @author Maximilian Käfer
|
||||
@ -46,6 +45,7 @@ public class ContactSearchTab implements EventListener {
|
||||
|
||||
private final Alert alert = new Alert(AlertType.CONFIRMATION);
|
||||
|
||||
private static final Client client = Context.getInstance().getClient();
|
||||
private static final EventBus eventBus = EventBus.getInstance();
|
||||
private static final Logger logger = EnvoyLog.getLogger(ChatScene.class);
|
||||
|
||||
@ -77,7 +77,7 @@ public class ContactSearchTab implements EventListener {
|
||||
@FXML
|
||||
private void sendRequest() {
|
||||
final var text = searchBar.getText().strip();
|
||||
if (!text.isBlank()) eventBus.dispatch(new SendEvent(new UserSearchRequest(text)));
|
||||
if (!text.isBlank()) client.send(new UserSearchRequest(text));
|
||||
else userList.getItems().clear();
|
||||
}
|
||||
|
||||
@ -106,7 +106,7 @@ public class ContactSearchTab implements EventListener {
|
||||
currentlySelectedUser = user;
|
||||
final var event = new ContactOperation(currentlySelectedUser, ElementOperation.ADD);
|
||||
// Sends the event to the server
|
||||
eventBus.dispatch(new SendEvent(event));
|
||||
client.send(event);
|
||||
// Removes the chosen user and updates the UI
|
||||
userList.getItems().remove(currentlySelectedUser);
|
||||
eventBus.dispatch(event);
|
||||
|
@ -10,8 +10,9 @@ import javafx.scene.control.*;
|
||||
import javafx.scene.layout.HBox;
|
||||
|
||||
import envoy.client.data.*;
|
||||
import envoy.client.event.*;
|
||||
import envoy.client.ui.listcell.*;
|
||||
import envoy.client.event.BackEvent;
|
||||
import envoy.client.ui.control.ContactControl;
|
||||
import envoy.client.ui.listcell.ListCellFactory;
|
||||
import envoy.data.*;
|
||||
import envoy.event.GroupCreation;
|
||||
import envoy.event.contact.ContactOperation;
|
||||
@ -27,10 +28,6 @@ import dev.kske.eventbus.*;
|
||||
* When the group creation button is pressed, a {@link GroupCreation} is sent to
|
||||
* the server. This controller enforces a valid group name and a non-empty
|
||||
* member list (excluding the client user).
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>GroupCreationScene.java</strong><br>
|
||||
* Created: <strong>07.06.2020</strong><br>
|
||||
*
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy Client v0.1-beta
|
||||
@ -137,8 +134,9 @@ public class GroupCreationTab implements EventListener {
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
private void createGroup(String name) {
|
||||
eventBus.dispatch(new SendEvent(
|
||||
new GroupCreation(name, userList.getSelectionModel().getSelectedItems().stream().map(User::getID).collect(Collectors.toSet()))));
|
||||
Context.getInstance()
|
||||
.getClient()
|
||||
.send(new GroupCreation(name, userList.getSelectionModel().getSelectedItems().stream().map(User::getID).collect(Collectors.toSet())));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -150,12 +148,7 @@ public class GroupCreationTab implements EventListener {
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public boolean groupNameAlreadyPresent(String newName) {
|
||||
return localDB.getChats()
|
||||
.stream()
|
||||
.map(Chat::getRecipient)
|
||||
.filter(Group.class::isInstance)
|
||||
.map(Contact::getName)
|
||||
.anyMatch(newName::equals);
|
||||
return localDB.getChats().stream().map(Chat::getRecipient).filter(Group.class::isInstance).map(Contact::getName).anyMatch(newName::equals);
|
||||
}
|
||||
|
||||
@FXML
|
||||
@ -211,7 +204,7 @@ public class GroupCreationTab implements EventListener {
|
||||
userList.getItems().add((User) operation.get());
|
||||
break;
|
||||
case REMOVE:
|
||||
userList.getItems().removeIf(u -> u.equals(operation.get()));
|
||||
userList.getItems().removeIf(operation.get()::equals);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
@ -12,6 +12,7 @@ import javafx.scene.image.ImageView;
|
||||
|
||||
import envoy.client.data.ClientConfig;
|
||||
import envoy.client.ui.*;
|
||||
import envoy.client.util.IconUtil;
|
||||
import envoy.data.LoginCredentials;
|
||||
import envoy.event.HandshakeRejection;
|
||||
import envoy.util.*;
|
||||
@ -19,9 +20,7 @@ import envoy.util.*;
|
||||
import dev.kske.eventbus.*;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>LoginDialog.java</strong><br>
|
||||
* Created: <strong>03.04.2020</strong><br>
|
||||
* Controller for the login scene.
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @author Maximilian Käfer
|
||||
@ -101,20 +100,21 @@ public final class LoginScene implements EventListener {
|
||||
|
||||
@FXML
|
||||
private void registerSwitchPressed() {
|
||||
|
||||
// Update button text and register switch
|
||||
if (!registration) {
|
||||
// case if the current mode is login
|
||||
loginButton.setText("Register");
|
||||
loginButton.setPadding(new Insets(2, 116, 2, 116));
|
||||
registerTextLabel.setText("Already an account?");
|
||||
registerSwitch.setText("Login");
|
||||
} else {
|
||||
// case if the current mode is registration
|
||||
loginButton.setText("Login");
|
||||
loginButton.setPadding(new Insets(2, 125, 2, 125));
|
||||
registerTextLabel.setText("No account yet?");
|
||||
registerSwitch.setText("Register");
|
||||
}
|
||||
registration = !registration;
|
||||
|
||||
// Make repeat password field and label visible / invisible
|
||||
repeatPasswordField.setVisible(registration);
|
||||
offlineModeButton.setDisable(registration);
|
||||
|
@ -1,20 +1,14 @@
|
||||
package envoy.client.ui.controller;
|
||||
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ListView;
|
||||
import javafx.scene.control.TitledPane;
|
||||
import javafx.scene.control.*;
|
||||
|
||||
import envoy.client.data.Context;
|
||||
import envoy.client.net.Client;
|
||||
import envoy.client.ui.SceneContext;
|
||||
import envoy.client.ui.listcell.AbstractListCell;
|
||||
import envoy.client.ui.listcell.ListCellFactory;
|
||||
import envoy.client.ui.settings.*;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>SettingsSceneController.java</strong><br>
|
||||
* Created: <strong>10.04.2020</strong><br>
|
||||
* Controller for the settings scene.
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
@ -27,20 +21,10 @@ public final class SettingsScene {
|
||||
@FXML
|
||||
private TitledPane titledPane;
|
||||
|
||||
private final Client client = Context.getInstance().getClient();
|
||||
private final SceneContext sceneContext = Context.getInstance().getSceneContext();
|
||||
|
||||
@FXML
|
||||
private void initialize() {
|
||||
settingsList.setCellFactory(listView -> new AbstractListCell<>(listView) {
|
||||
|
||||
@Override
|
||||
protected Label renderItem(SettingsPane item) { return new Label(item.getTitle()); }
|
||||
});
|
||||
settingsList.getItems().add(new GeneralSettingsPane());
|
||||
settingsList.getItems().add(new UserSettingsPane(sceneContext, client.getSender(), client.isOnline()));
|
||||
settingsList.getItems().add(new DownloadSettingsPane(sceneContext));
|
||||
settingsList.getItems().add(new BugReportPane(client.getSender(), client.isOnline()));
|
||||
settingsList.setCellFactory(new ListCellFactory<>(pane -> new Label(pane.getTitle())));
|
||||
settingsList.getItems().addAll(new GeneralSettingsPane(), new UserSettingsPane(), new DownloadSettingsPane(), new BugReportPane());
|
||||
}
|
||||
|
||||
@FXML
|
||||
@ -53,5 +37,5 @@ public final class SettingsScene {
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void backButtonClicked() { sceneContext.pop(); }
|
||||
private void backButtonClicked() { Context.getInstance().getSceneContext().pop(); }
|
||||
}
|
||||
|
@ -2,10 +2,6 @@ package envoy.client.ui.controller;
|
||||
|
||||
/**
|
||||
* Provides options to select different tabs.
|
||||
* <p>
|
||||
* Project: <strong>client</strong><br>
|
||||
* File: <strong>Tabs.java</strong><br>
|
||||
* Created: <strong>30.8.2020</strong><br>
|
||||
*
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy Client v0.2-beta
|
||||
|
@ -1,11 +1,9 @@
|
||||
/**
|
||||
* Contains JavaFX scene controllers.
|
||||
* <p>
|
||||
* 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
|
||||
* @author Leon Hofmeister
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
package envoy.client.ui.controller;
|
||||
|
@ -1,14 +0,0 @@
|
||||
/**
|
||||
* This package stores custom components for use in JavaFX.
|
||||
* These components are also expected to be used via FXML.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>package-info.java</strong><br>
|
||||
* Created: <strong>30.07.2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @author Maximilian Käfer
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.2-beta
|
||||
*/
|
||||
package envoy.client.ui.custom;
|
@ -1,17 +1,10 @@
|
||||
package envoy.client.ui.listcell;
|
||||
|
||||
import javafx.scene.Cursor;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.ContentDisplay;
|
||||
import javafx.scene.control.ListCell;
|
||||
import javafx.scene.control.ListView;
|
||||
import javafx.scene.*;
|
||||
import javafx.scene.control.*;
|
||||
|
||||
/**
|
||||
* Provides a convenience frame for list cell creation.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>AbstractListCell.java</strong><br>
|
||||
* Created: <strong>18.07.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @param <T> the type of element displayed by the list cell
|
||||
|
@ -7,10 +7,6 @@ import javafx.scene.control.ListView;
|
||||
|
||||
/**
|
||||
* A generic list cell rendering an item using a provided render function.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>GenericListCell.java</strong><br>
|
||||
* Created: <strong>18.07.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @param <T> the type of element displayed by the list cell
|
||||
|
@ -3,17 +3,12 @@ package envoy.client.ui.listcell;
|
||||
import java.util.function.Function;
|
||||
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.ListCell;
|
||||
import javafx.scene.control.ListView;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.util.Callback;
|
||||
|
||||
/**
|
||||
* Provides a creation mechanism for generic list cells given a list view and a
|
||||
* conversion function.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>ListCellFactory.java</strong><br>
|
||||
* Created: <strong>13.07.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @param <T> the type of object to display
|
||||
|
@ -1,17 +1,13 @@
|
||||
package envoy.client.ui.listcell;
|
||||
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.geometry.*;
|
||||
import javafx.scene.control.ListView;
|
||||
|
||||
import envoy.client.ui.control.MessageControl;
|
||||
import envoy.data.Message;
|
||||
|
||||
/**
|
||||
* A list cell containing messages represented as message controls.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>MessageListCell.java</strong><br>
|
||||
* Created: <strong>18.07.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
|
@ -1,12 +1,9 @@
|
||||
/**
|
||||
* This package contains custom list cells that are used to display certain
|
||||
* things.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>package-info.java</strong><br>
|
||||
* Created: <strong>30.06.2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
package envoy.client.ui.listcell;
|
||||
|
@ -4,24 +4,17 @@ import javafx.event.EventHandler;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.input.InputEvent;
|
||||
|
||||
import envoy.client.event.SendEvent;
|
||||
import envoy.client.data.Context;
|
||||
import envoy.client.util.IssueUtil;
|
||||
import envoy.data.User;
|
||||
|
||||
import dev.kske.eventbus.EventBus;
|
||||
|
||||
/**
|
||||
* This class offers the option for users to submit a bug report. Only the title
|
||||
* of a bug is needed to be sent.
|
||||
* <p>
|
||||
* Project: <strong>client</strong><br>
|
||||
* File: <strong>BugReportPane.java</strong><br>
|
||||
* Created: <strong>Aug 4, 2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Client v0.2-beta
|
||||
*/
|
||||
public final class BugReportPane extends OnlyIfOnlineSettingsPane {
|
||||
public final class BugReportPane extends OnlineOnlySettingsPane {
|
||||
|
||||
private final Label titleLabel = new Label("Suggest a title for the bug:");
|
||||
private final TextField titleTextField = new TextField();
|
||||
@ -35,12 +28,10 @@ public final class BugReportPane extends OnlyIfOnlineSettingsPane {
|
||||
/**
|
||||
* Creates a new {@code BugReportPane}.
|
||||
*
|
||||
* @param user the user whose details to use
|
||||
* @param online whether this user is currently online
|
||||
* @since Envoy Client v0.2-beta
|
||||
*/
|
||||
public BugReportPane(User user, boolean online) {
|
||||
super("Report a bug", online);
|
||||
public BugReportPane() {
|
||||
super("Report a bug");
|
||||
setSpacing(10);
|
||||
setToolTipText("A bug can only be reported while being online");
|
||||
|
||||
@ -67,13 +58,10 @@ public final class BugReportPane extends OnlyIfOnlineSettingsPane {
|
||||
|
||||
// Displaying the submitReportButton
|
||||
submitReportButton.setDisable(true);
|
||||
submitReportButton.setOnAction(e -> {
|
||||
EventBus.getInstance()
|
||||
.dispatch(new SendEvent(IssueUtil.createIssueProposal(titleTextField.getText(),
|
||||
submitReportButton.setOnAction(e -> client.send(IssueUtil.createIssueProposal(titleTextField.getText(),
|
||||
errorDetailArea.getText(),
|
||||
showUsernameInBugReport.isSelected() ? user.getName() : null,
|
||||
showUsernameInBugReport.isSelected() ? Context.getInstance().getLocalDB().getUser().getName() : null,
|
||||
true)));
|
||||
});
|
||||
getChildren().add(submitReportButton);
|
||||
}
|
||||
}
|
||||
|
@ -5,14 +5,10 @@ import javafx.scene.control.*;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.stage.DirectoryChooser;
|
||||
|
||||
import envoy.client.ui.SceneContext;
|
||||
import envoy.client.data.Context;
|
||||
|
||||
/**
|
||||
* Displays options for downloading {@link envoy.data.Attachment}s.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>DownloadSettingsPane.java</strong><br>
|
||||
* Created: <strong>27.07.2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Client v0.2-beta
|
||||
@ -22,15 +18,14 @@ public final class DownloadSettingsPane extends SettingsPane {
|
||||
/**
|
||||
* Constructs a new {@code DownloadSettingsPane}.
|
||||
*
|
||||
* @param sceneContext the {@code SceneContext} used to block input to the
|
||||
* {@link javafx.stage.Stage} used in Envoy
|
||||
* @since Envoy Client v0.2-beta
|
||||
*/
|
||||
public DownloadSettingsPane(SceneContext sceneContext) {
|
||||
public DownloadSettingsPane() {
|
||||
super("Download");
|
||||
setSpacing(15);
|
||||
setPadding(new Insets(15));
|
||||
// checkbox to disable asking
|
||||
|
||||
// Checkbox to disable asking
|
||||
final var checkBox = new CheckBox(settings.getItems().get("autoSaveDownloads").getUserFriendlyName());
|
||||
checkBox.setSelected(settings.isDownloadSavedWithoutAsking());
|
||||
checkBox.setTooltip(new Tooltip("Determines whether a \"Select save location\" - dialogue will be shown when saving attachments."));
|
||||
@ -52,7 +47,7 @@ public final class DownloadSettingsPane extends SettingsPane {
|
||||
final var directoryChooser = new DirectoryChooser();
|
||||
directoryChooser.setTitle("Select the directory where attachments should be saved to");
|
||||
directoryChooser.setInitialDirectory(settings.getDownloadLocation());
|
||||
final var selectedDirectory = directoryChooser.showDialog(sceneContext.getStage());
|
||||
final var selectedDirectory = directoryChooser.showDialog(Context.getInstance().getSceneContext().getStage());
|
||||
|
||||
if (selectedDirectory != null) {
|
||||
currentPath.setText(selectedDirectory.getAbsolutePath());
|
||||
|
@ -9,10 +9,6 @@ import envoy.data.User.UserStatus;
|
||||
import dev.kske.eventbus.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
|
||||
*/
|
||||
|
@ -1,40 +1,39 @@
|
||||
package envoy.client.ui.settings;
|
||||
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.layout.Background;
|
||||
import javafx.scene.layout.BackgroundFill;
|
||||
import javafx.scene.layout.CornerRadii;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.scene.paint.Color;
|
||||
|
||||
import envoy.client.data.Context;
|
||||
import envoy.client.net.Client;
|
||||
|
||||
/**
|
||||
* Inheriting from this class signifies that options should only be available if
|
||||
* the {@link envoy.data.User} is currently online. If the user is currently
|
||||
* offline, all {@link javafx.scene.Node} variables will be disabled and a
|
||||
* {@link Tooltip} will be displayed for the whole node.
|
||||
* <p>
|
||||
* Project: <strong>client</strong><br>
|
||||
* File: <strong>OnlyIfOnlineSettingsPane.java</strong><br>
|
||||
* Created: <strong>04.08.2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.2-beta
|
||||
*/
|
||||
public abstract class OnlyIfOnlineSettingsPane extends SettingsPane {
|
||||
public abstract class OnlineOnlySettingsPane extends SettingsPane {
|
||||
|
||||
protected final Client client = Context.getInstance().getClient();
|
||||
|
||||
private final Tooltip beOnlineReminder = new Tooltip("You need to be online to modify your account.");
|
||||
|
||||
/**
|
||||
* @param title
|
||||
* @param title the title of this pane
|
||||
* @since Envoy Client v0.2-beta
|
||||
*/
|
||||
protected OnlyIfOnlineSettingsPane(String title, boolean online) {
|
||||
protected OnlineOnlySettingsPane(String title) {
|
||||
super(title);
|
||||
|
||||
setDisable(!online);
|
||||
setDisable(!client.isOnline());
|
||||
|
||||
if (!online) {
|
||||
if (!client.isOnline()) {
|
||||
final var infoLabel = new Label("You shall not pass!\n(... Unless you would happen to be online)");
|
||||
infoLabel.setId("info-label-warning");
|
||||
infoLabel.setWrapText(true);
|
||||
@ -45,5 +44,11 @@ public abstract class OnlyIfOnlineSettingsPane extends SettingsPane {
|
||||
} else Tooltip.uninstall(this, beOnlineReminder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the text of the tooltip displayed for this pane.
|
||||
*
|
||||
* @param text the text to display
|
||||
* @since Envoy Client v0.2-beta
|
||||
*/
|
||||
protected void setToolTipText(String text) { beOnlineReminder.setText(text); }
|
||||
}
|
@ -6,10 +6,6 @@ import javafx.scene.control.CheckBox;
|
||||
import envoy.client.data.SettingsItem;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>SettingsToggleButton.java</strong><br>
|
||||
* Created: <strong>18.04.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
|
@ -5,10 +5,6 @@ import javafx.scene.layout.VBox;
|
||||
import envoy.client.data.Settings;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>SettingsPane.java</strong><br>
|
||||
* Created: <strong>18.04.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
|
@ -14,24 +14,19 @@ import javafx.scene.input.InputEvent;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.stage.FileChooser;
|
||||
|
||||
import envoy.client.event.SendEvent;
|
||||
import envoy.client.ui.*;
|
||||
import envoy.client.ui.custom.ProfilePicImageView;
|
||||
import envoy.data.User;
|
||||
import envoy.client.data.Context;
|
||||
import envoy.client.ui.control.ProfilePicImageView;
|
||||
import envoy.client.util.IconUtil;
|
||||
import envoy.event.*;
|
||||
import envoy.util.*;
|
||||
|
||||
import dev.kske.eventbus.EventBus;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>UserSettingsPane.java</strong><br>
|
||||
* Created: <strong>31.07.2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Client v0.2-beta
|
||||
*/
|
||||
public final class UserSettingsPane extends OnlyIfOnlineSettingsPane {
|
||||
public final class UserSettingsPane extends OnlineOnlySettingsPane {
|
||||
|
||||
private boolean profilePicChanged, usernameChanged, validPassword;
|
||||
private byte[] currentImageBytes;
|
||||
@ -50,13 +45,10 @@ public final class UserSettingsPane extends OnlyIfOnlineSettingsPane {
|
||||
/**
|
||||
* Creates a new {@code UserSettingsPane}.
|
||||
*
|
||||
* @param sceneContext the {@code SceneContext} to block input to Envoy
|
||||
* @param user the user who wants to customize his profile
|
||||
* @param online whether this user is currently online
|
||||
* @since Envoy Client v0.2-beta
|
||||
*/
|
||||
public UserSettingsPane(SceneContext sceneContext, User user, boolean online) {
|
||||
super("User", online);
|
||||
public UserSettingsPane() {
|
||||
super("User");
|
||||
setSpacing(10);
|
||||
|
||||
// Display of profile pic change mechanism
|
||||
@ -67,18 +59,19 @@ public final class UserSettingsPane extends OnlyIfOnlineSettingsPane {
|
||||
profilePic.setFitWidth(60);
|
||||
profilePic.setFitHeight(60);
|
||||
profilePic.setOnMouseClicked(e -> {
|
||||
if (!online) return;
|
||||
if (!client.isOnline()) return;
|
||||
final var pictureChooser = new FileChooser();
|
||||
|
||||
pictureChooser.setTitle("Select a new profile pic");
|
||||
pictureChooser.setInitialDirectory(new File(System.getProperty("user.home")));
|
||||
pictureChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Pictures", "*.png", "*.jpg", "*.bmp", "*.gif"));
|
||||
|
||||
final var file = pictureChooser.showOpenDialog(sceneContext.getStage());
|
||||
final var file = pictureChooser.showOpenDialog(Context.getInstance().getSceneContext().getStage());
|
||||
|
||||
if (file != null) {
|
||||
|
||||
// Check max file size
|
||||
// TODO: Move to config
|
||||
if (file.length() > 5E6) {
|
||||
new Alert(AlertType.WARNING, "The selected file exceeds the size limit of 5MB!").showAndWait();
|
||||
return;
|
||||
@ -96,7 +89,7 @@ public final class UserSettingsPane extends OnlyIfOnlineSettingsPane {
|
||||
hbox.getChildren().add(profilePic);
|
||||
|
||||
// Displaying the username change mechanism
|
||||
final var username = user.getName();
|
||||
final var username = client.getSender().getName();
|
||||
newUsername = username;
|
||||
usernameTextField.setText(username);
|
||||
final EventHandler<? super InputEvent> textChanged = e -> {
|
||||
@ -133,7 +126,7 @@ public final class UserSettingsPane extends OnlyIfOnlineSettingsPane {
|
||||
}
|
||||
|
||||
// Displaying the save button
|
||||
saveButton.setOnAction(e -> save(user.getID(), currentPasswordField.getText()));
|
||||
saveButton.setOnAction(e -> save(client.getSender().getID(), currentPasswordField.getText()));
|
||||
saveButton.setAlignment(Pos.BOTTOM_RIGHT);
|
||||
getChildren().add(saveButton);
|
||||
}
|
||||
@ -150,7 +143,7 @@ public final class UserSettingsPane extends OnlyIfOnlineSettingsPane {
|
||||
if (profilePicChanged) {
|
||||
final var profilePicChangeEvent = new ProfilePicChange(currentImageBytes, userID);
|
||||
eventBus.dispatch(profilePicChangeEvent);
|
||||
eventBus.dispatch(new SendEvent(profilePicChangeEvent));
|
||||
client.send(profilePicChangeEvent);
|
||||
logger.log(Level.INFO, "The user just changed his profile pic.");
|
||||
}
|
||||
|
||||
@ -158,8 +151,8 @@ public final class UserSettingsPane extends OnlyIfOnlineSettingsPane {
|
||||
final var validContactName = Bounds.isValidContactName(newUsername);
|
||||
if (usernameChanged && validContactName) {
|
||||
final var nameChangeEvent = new NameChange(userID, newUsername);
|
||||
eventBus.dispatch(new SendEvent(nameChangeEvent));
|
||||
eventBus.dispatch(nameChangeEvent);
|
||||
client.send(nameChangeEvent);
|
||||
logger.log(Level.INFO, "The user just changed his name to " + newUsername + ".");
|
||||
} else if (!validContactName) {
|
||||
final var alert = new Alert(AlertType.ERROR);
|
||||
@ -172,14 +165,13 @@ public final class UserSettingsPane extends OnlyIfOnlineSettingsPane {
|
||||
|
||||
// The password was changed
|
||||
if (validPassword) {
|
||||
eventBus.dispatch(new SendEvent(new PasswordChangeRequest(newPassword, oldPassword, userID)));
|
||||
client.send(new PasswordChangeRequest(newPassword, oldPassword, userID));
|
||||
logger.log(Level.INFO, "The user just tried to change his password!");
|
||||
} else if (!(validPassword || newPassword.isBlank())) {
|
||||
final var alert = new Alert(AlertType.ERROR);
|
||||
alert.setTitle("Unequal Password");
|
||||
alert.setContentText("Repeated password is unequal to the chosen new password");
|
||||
alert.showAndWait();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,6 @@
|
||||
/**
|
||||
* This package contains classes used for representing the settings
|
||||
* visually.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>package-info.java</strong><br>
|
||||
* Created: <strong>19 Apr 2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @author Kai S. K. Engelbart
|
||||
|
@ -1,9 +1,8 @@
|
||||
package envoy.client.ui;
|
||||
package envoy.client.util;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.util.EnumMap;
|
||||
import java.util.EnumSet;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
@ -16,10 +15,6 @@ import envoy.util.EnvoyLog;
|
||||
/**
|
||||
* Provides static utility methods for loading icons from the resource
|
||||
* folder.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>IconUtil.java</strong><br>
|
||||
* Created: <strong>16.03.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
@ -35,15 +30,7 @@ public final class IconUtil {
|
||||
* @return the loaded image
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public static Image load(String path) {
|
||||
Image image = null;
|
||||
try {
|
||||
image = new Image(IconUtil.class.getResource(path).toExternalForm());
|
||||
} catch (final NullPointerException e) {
|
||||
EnvoyLog.getLogger(IconUtil.class).log(Level.WARNING, String.format("Could not load image at path %s: ", path), e);
|
||||
}
|
||||
return image;
|
||||
}
|
||||
public static Image load(String path) { return new Image(IconUtil.class.getResource(path).toExternalForm()); }
|
||||
|
||||
/**
|
||||
* Loads an image from the resource folder and scales it to the given size.
|
||||
@ -54,13 +41,7 @@ public final class IconUtil {
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public static Image load(String path, int size) {
|
||||
Image image = null;
|
||||
try {
|
||||
image = new Image(IconUtil.class.getResource(path).toExternalForm(), size, size, true, true);
|
||||
} catch (final NullPointerException e) {
|
||||
EnvoyLog.getLogger(IconUtil.class).log(Level.WARNING, String.format("Could not load image at path %s: ", path), e);
|
||||
}
|
||||
return image;
|
||||
return new Image(IconUtil.class.getResource(path).toExternalForm(), size, size, true, true);
|
||||
}
|
||||
|
||||
/**
|
@ -34,7 +34,6 @@ public final class IssueUtil {
|
||||
title = sanitizeIssueTitle(title);
|
||||
description = sanitizeIssueDescription(description, username);
|
||||
return new IssueProposal(title, description, isBug);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -65,8 +64,6 @@ public final class IssueUtil {
|
||||
* {@code null} if he does not want to be named.
|
||||
* @return the sanitized description
|
||||
* @since Envoy Client v0.2-beta
|
||||
* @apiNote the String returned might not be sanitized in case multiple
|
||||
* backslashes are preceding a quote.
|
||||
*/
|
||||
public static String sanitizeIssueDescription(String description, String username) {
|
||||
|
||||
|
@ -1,17 +1,11 @@
|
||||
package envoy.client.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.*;
|
||||
import java.util.stream.*;
|
||||
|
||||
import javafx.scene.Node;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>ReflectionUtil.java</strong><br>
|
||||
* Created: <strong>02.08.2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Client v0.2-beta
|
||||
*/
|
||||
@ -20,8 +14,9 @@ public final class ReflectionUtil {
|
||||
private ReflectionUtil() {}
|
||||
|
||||
/**
|
||||
* Gets all declared variables of the given instance that have the specified
|
||||
* class<br>
|
||||
* Gets all declared variable values of the given instance that have the
|
||||
* specified class.
|
||||
* <p>
|
||||
* (i.e. can get all {@code JComponents} (Swing) or {@code Nodes} (JavaFX) in a
|
||||
* GUI class).
|
||||
* <p>
|
||||
@ -41,13 +36,11 @@ public final class ReflectionUtil {
|
||||
return Arrays.stream(instance.getClass().getDeclaredFields()).filter(field -> typeToReturn.isAssignableFrom(field.getType())).map(field -> {
|
||||
try {
|
||||
field.setAccessible(true);
|
||||
final var value = field.get(instance);
|
||||
return value;
|
||||
return typeToReturn.cast(field.get(instance));
|
||||
} catch (IllegalArgumentException | IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}).map(typeToReturn::cast);// field ->
|
||||
// typeToReturn.isAssignableFrom(field.getClass())).map(typeToReturn::cast);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,9 +1,5 @@
|
||||
/**
|
||||
* This package contains utility classes for use in envoy-client.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>package-info.java</strong><br>
|
||||
* Created: <strong>02.08.2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Client v0.2-beta
|
||||
|
@ -20,7 +20,7 @@ module envoy.client {
|
||||
|
||||
opens envoy.client.ui to javafx.graphics, javafx.fxml, dev.kske.eventbus;
|
||||
opens envoy.client.ui.controller to javafx.graphics, javafx.fxml, envoy.client.util, dev.kske.eventbus;
|
||||
opens envoy.client.ui.custom to javafx.graphics, javafx.fxml;
|
||||
opens envoy.client.ui.control to javafx.graphics, javafx.fxml;
|
||||
opens envoy.client.ui.settings to envoy.client.util;
|
||||
opens envoy.client.net to dev.kske.eventbus;
|
||||
opens envoy.client.data to dev.kske.eventbus;
|
||||
|
@ -5,10 +5,6 @@ import java.io.Serializable;
|
||||
/**
|
||||
* This interface should be used for any type supposed to be a {@link Message}
|
||||
* attachment (i.e. images or sound).
|
||||
* <p>
|
||||
* Project: <strong>envoy-common</strong><br>
|
||||
* File: <strong>Attachment.java</strong><br>
|
||||
* Created: <strong>30 Dec 2019</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @author Kai S. K. Engelbart
|
||||
|
@ -1,7 +1,6 @@
|
||||
package envoy.data;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.logging.Level;
|
||||
@ -19,10 +18,6 @@ import envoy.util.EnvoyLog;
|
||||
* default value or over command line argument. Developers that fail to provide
|
||||
* default values will be greeted with an error message the next time they try
|
||||
* to start Envoy...
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>Config.java</strong><br>
|
||||
* Created: <strong>12 Oct 2019</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Common v0.1-beta
|
||||
@ -104,6 +99,7 @@ public class Config {
|
||||
public void loadAll(Class<?> declaringClass, String propertiesFilePath, String[] args) {
|
||||
if (modificationDisabled)
|
||||
throw new IllegalStateException("Cannot change config after isInitialized has been called");
|
||||
|
||||
// Load the defaults from the given .properties file first
|
||||
final var properties = new Properties();
|
||||
try {
|
||||
@ -120,6 +116,7 @@ public class Config {
|
||||
|
||||
// Check if all configuration values have been initialized
|
||||
isInitialized();
|
||||
|
||||
// Disable further editing of the config
|
||||
modificationDisabled = true;
|
||||
}
|
||||
@ -130,10 +127,9 @@ public class Config {
|
||||
* @since Envoy Common v0.1-beta
|
||||
*/
|
||||
private void isInitialized() {
|
||||
if (items.values().stream().map(ConfigItem::get).anyMatch(Objects::isNull))
|
||||
throw new IllegalStateException("config item(s) has/ have not been initialized:"
|
||||
+ items.values().stream().filter(configItem -> configItem.get() == null)
|
||||
.map(ConfigItem::getCommandLong).collect(Collectors.toSet()));
|
||||
String uninitialized = items.values().stream().filter(c -> c.get() == null).map(ConfigItem::getCommandLong).collect(Collectors.joining(", "));
|
||||
if(!uninitialized.isEmpty())
|
||||
throw new IllegalStateException("Config items uninitialized: " + uninitialized);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -7,10 +7,6 @@ import java.util.function.Function;
|
||||
* line arguments and its default value.
|
||||
* <p>
|
||||
* All {@code ConfigItem}s are automatically mandatory.
|
||||
* <p>
|
||||
* Project: <strong>envoy-clientChess</strong><br>
|
||||
* File: <strong>ConfigItem.javaEvent.java</strong><br>
|
||||
* Created: <strong>21.12.2019</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @param <T> the type of the config item's value
|
||||
|
@ -1,16 +1,11 @@
|
||||
package envoy.data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* This class is the superclass for both {@link User} and {@link Group}.<br>
|
||||
* It provides an id and a name for each user and group.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-common</strong><br>
|
||||
* File: <strong>Contact.java</strong><br>
|
||||
* Created: <strong>24 Mar 2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy v0.1-beta
|
||||
|
@ -1,15 +1,9 @@
|
||||
package envoy.data;
|
||||
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-common</strong><br>
|
||||
* File: <strong>Group.java</strong><br>
|
||||
* Created: <strong>24 Mar 2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Common v0.1-beta
|
||||
*/
|
||||
|
@ -1,14 +1,9 @@
|
||||
package envoy.data;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-common</strong><br>
|
||||
* File: <strong>GroupMessage.java</strong><br>
|
||||
* Created: <strong>26.03.2020</strong><br>
|
||||
*
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy Common v0.1-beta
|
||||
*/
|
||||
|
@ -2,17 +2,15 @@ package envoy.data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import dev.kske.eventbus.IEvent;
|
||||
|
||||
/**
|
||||
* Generates increasing IDs between two numbers.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-common</strong><br>
|
||||
* File: <strong>IDGenerator.java</strong><br>
|
||||
* Created: <strong>31.12.2019</strong><br>
|
||||
* Generates increasing IDs between two numbers.
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Common v0.2-alpha
|
||||
*/
|
||||
public final class IDGenerator implements Serializable {
|
||||
public final class IDGenerator implements IEvent, Serializable {
|
||||
|
||||
private final long end;
|
||||
private long current;
|
||||
|
@ -9,10 +9,6 @@ import java.time.Instant;
|
||||
* <p>
|
||||
* If the authentication is performed with a token, the token is stored instead
|
||||
* of the password.
|
||||
* <p>
|
||||
* Project: <strong>envoy-common</strong><br>
|
||||
* File: <strong>LoginCredentials.java</strong><br>
|
||||
* Created: <strong>29.12.2019</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Common v0.2-alpha
|
||||
|
@ -9,10 +9,6 @@ import dev.kske.eventbus.IEvent;
|
||||
* Represents a unique message with a unique, numeric ID. Further metadata
|
||||
* includes the sender and recipient {@link User}s, as well as the creation
|
||||
* date and the current {@link MessageStatus}.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-common</strong><br>
|
||||
* File: <strong>Message.java</strong><br>
|
||||
* Created: <strong>28.12.2019</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @author Leon Hofmeister
|
||||
@ -28,23 +24,22 @@ public class Message implements Serializable, IEvent {
|
||||
public enum MessageStatus {
|
||||
|
||||
/**
|
||||
* is selected, if a message was sent but not received by the server yet.
|
||||
* The message has not yet been sent to the server
|
||||
*/
|
||||
WAITING,
|
||||
|
||||
/**
|
||||
* is selected, if a sent message was received by the server.
|
||||
* The message has been sent to the server.
|
||||
*/
|
||||
SENT,
|
||||
|
||||
/**
|
||||
* is selected, if a message was delivered from the server to the recipient, but
|
||||
* has not been read yet.
|
||||
* The message has been received by its recipient.
|
||||
*/
|
||||
RECEIVED,
|
||||
|
||||
/**
|
||||
* is selected, if a recipient opened the corresponding chat of said message.
|
||||
* The message has been read by its recipient.
|
||||
*/
|
||||
READ
|
||||
}
|
||||
|
@ -7,10 +7,6 @@ import envoy.data.Message.MessageStatus;
|
||||
|
||||
/**
|
||||
* Provides a method of constructing the {@link Message} class.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-common</strong><br>
|
||||
* File: <strong>MessageBuilder.java</strong><br>
|
||||
* Created: <strong>31.12.2019</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Common v0.2-alpha
|
||||
|
@ -1,17 +1,11 @@
|
||||
package envoy.data;
|
||||
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Represents a unique user with a unique, numeric ID, a name and a current
|
||||
* {@link UserStatus}.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-common</strong><br>
|
||||
* File: <strong>User.java</strong><br>
|
||||
* Created: <strong>28.12.2019</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Common v0.2-alpha
|
||||
@ -32,7 +26,7 @@ public final class User extends Contact {
|
||||
*
|
||||
* @since Envoy Common v0.2-alpha
|
||||
*/
|
||||
public static enum UserStatus {
|
||||
public enum UserStatus {
|
||||
|
||||
/**
|
||||
* select this, if a user is online and can be interacted with
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* This package contains all data objects that are used both by Envoy Client and
|
||||
* by Envoy Server Standalone.
|
||||
* by Envoy Server.
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @author Maximilian Käfer
|
||||
|
@ -1,13 +1,10 @@
|
||||
package envoy.event;
|
||||
|
||||
/**
|
||||
* This enum declares all modification possibilities for a given container.<br>
|
||||
* This enum declares all modification possibilities for a given container.
|
||||
* <p>
|
||||
* These can be: {@link ElementOperation#ADD} or
|
||||
* {@link ElementOperation#REMOVE}.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-common</strong><br>
|
||||
* File: <strong>ElementOperation.java</strong><br>
|
||||
* Created: <strong>25 Mar 2020</strong><br>
|
||||
* {@link ElementOperation#REMOVE}.
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Common v0.1-beta
|
||||
|
@ -8,10 +8,6 @@ import dev.kske.eventbus.IEvent;
|
||||
* This class serves as a convenience base class for all events. It implements
|
||||
* the {@link IEvent} interface and provides a generic value. For events without
|
||||
* a value there also is {@link envoy.event.Event.Valueless}.
|
||||
* <p>
|
||||
* Project: <strong>envoy-common</strong><br>
|
||||
* File: <strong>Event.java</strong><br>
|
||||
* Created: <strong>04.12.2019</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @param <T> the type of the Event
|
||||
@ -34,11 +30,7 @@ public abstract class Event<T> implements IEvent, Serializable {
|
||||
public String toString() { return String.format("%s[value=%s]", this.getClass().getSimpleName(), value); }
|
||||
|
||||
/**
|
||||
* Serves as a super class for events that do not carry a value.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-common</strong><br>
|
||||
* File: <strong>Event.java</strong><br>
|
||||
* Created: <strong>11 Feb 2020</strong><br>
|
||||
* Serves as a super class for events that do not carry a value.
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Common v0.2-alpha
|
||||
|
@ -1,16 +1,11 @@
|
||||
package envoy.event;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
import envoy.data.User;
|
||||
|
||||
/**
|
||||
* This event creates a group with the given name.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-common</strong><br>
|
||||
* File: <strong>GroupCreation.java</strong><br>
|
||||
* Created: <strong>25 Mar 2020</strong><br>
|
||||
* This event creates a group with the given name.
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Common v0.1-beta
|
||||
@ -30,7 +25,7 @@ public final class GroupCreation extends Event<String> {
|
||||
*/
|
||||
public GroupCreation(String value, Set<Long> initialMemberIDs) {
|
||||
super(value);
|
||||
this.initialMemberIDs = (initialMemberIDs != null) ? initialMemberIDs : new HashSet<>();
|
||||
this.initialMemberIDs = initialMemberIDs != null ? initialMemberIDs : new HashSet<>();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3,10 +3,6 @@ package envoy.event;
|
||||
/**
|
||||
* Used to communicate with a client that his request to create a group might
|
||||
* have been rejected as it might be disabled on his current server.
|
||||
* <p>
|
||||
* Project: <strong>common</strong><br>
|
||||
* File: <strong>GroupCreationResult.java</strong><br>
|
||||
* Created: <strong>22.08.2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Common v0.2-beta
|
||||
|
@ -6,10 +6,6 @@ import envoy.data.GroupMessage;
|
||||
import envoy.data.Message.MessageStatus;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-common</strong><br>
|
||||
* File: <strong>GroupMessageStatusChange.java</strong><br>
|
||||
* Created: <strong>18.04.2020</strong><br>
|
||||
*
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy Common v0.1-beta
|
||||
*/
|
||||
|
@ -1,18 +1,15 @@
|
||||
package envoy.event;
|
||||
|
||||
import envoy.data.Contact;
|
||||
import envoy.data.Group;
|
||||
import envoy.data.User;
|
||||
import static envoy.event.ElementOperation.*;
|
||||
|
||||
import envoy.data.*;
|
||||
|
||||
/**
|
||||
* This event is used to communicate changes in the group size between client
|
||||
* and server.<br>
|
||||
* and server.
|
||||
* <p>
|
||||
* Possible actions are adding or removing certain {@link User}s to or from a
|
||||
* certain {@link Group}.
|
||||
* <br>
|
||||
* Project: <strong>envoy-common</strong><br>
|
||||
* File: <strong>GroupResize.java</strong><br>
|
||||
* Created: <strong>25 Mar 2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Common v0.1-beta
|
||||
@ -36,13 +33,13 @@ public final class GroupResize extends Event<User> {
|
||||
*/
|
||||
public GroupResize(User user, Group group, ElementOperation operation) {
|
||||
super(user);
|
||||
if (group.getContacts().contains(user)) {
|
||||
if (operation.equals(ElementOperation.ADD)) throw new IllegalArgumentException(
|
||||
"Cannot add " + user + " to group " + group.getID() + " because he is already a member of this group");
|
||||
} else if (operation.equals(ElementOperation.REMOVE))
|
||||
throw new IllegalArgumentException("Cannot remove " + user + " from group " + group.getID() + " because he is no part of this group");
|
||||
groupID = group.getID();
|
||||
this.operation = operation;
|
||||
if (group.getContacts().contains(user)) {
|
||||
if (operation.equals(ADD))
|
||||
throw new IllegalArgumentException(String.format("Cannot add %s to %s!", user, group));
|
||||
} else if (operation.equals(REMOVE))
|
||||
throw new IllegalArgumentException(String.format("Cannot remove %s from %s!", user, group));
|
||||
groupID = group.getID();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -58,8 +55,22 @@ public final class GroupResize extends Event<User> {
|
||||
public ElementOperation getOperation() { return operation; }
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* Applies the operation to a group.
|
||||
*
|
||||
* @param group the group to resize
|
||||
* @since Envoy Common v0.2-beta
|
||||
*/
|
||||
public void apply(Group group) {
|
||||
switch (operation) {
|
||||
case ADD:
|
||||
group.getContacts().add(value);
|
||||
break;
|
||||
case REMOVE:
|
||||
group.getContacts().remove(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() { return String.format("GroupResize[userid=%d,groupid=%d,operation=%s]", get(), groupID, operation); }
|
||||
}
|
||||
|
@ -3,10 +3,6 @@ package envoy.event;
|
||||
/**
|
||||
* Signifies to the client that the handshake failed for the attached
|
||||
* reason.
|
||||
* <p>
|
||||
* Project: <strong>envoy-common</strong><br>
|
||||
* File: <strong>HandshakeRejection.java</strong><br>
|
||||
* Created: <strong>28 Jan 2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Common v0.3-alpha
|
||||
|
@ -2,11 +2,7 @@ package envoy.event;
|
||||
|
||||
/**
|
||||
* Signifies to the server that the client needs a new
|
||||
* {@link envoy.data.IDGenerator} instance.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-common</strong><br>
|
||||
* File: <strong>IDGeneratorRequest.java</strong><br>
|
||||
* Created: <strong>28 Jan 2020</strong><br>
|
||||
* {@link envoy.data.IDGenerator} instance.
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Common v0.3-alpha
|
||||
|
@ -3,10 +3,6 @@ package envoy.event;
|
||||
/**
|
||||
* This event should be sent when a user is currently typing something in a
|
||||
* chat.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>IsTyping.java</strong><br>
|
||||
* Created: <strong>24.07.2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Client v0.2-beta
|
||||
@ -18,7 +14,8 @@ public final class IsTyping extends Event<Long> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* The number of milliseconds that this event will be active.<br>
|
||||
* The number of milliseconds that this event will be active.
|
||||
* <p>
|
||||
* Currently set to 3.5 seconds.
|
||||
*
|
||||
* @since Envoy Common v0.2-beta
|
||||
@ -28,8 +25,8 @@ public final class IsTyping extends Event<Long> {
|
||||
/**
|
||||
* Creates a new {@code IsTyping} event with originator and recipient.
|
||||
*
|
||||
* @param sourceID the id of the originator
|
||||
* @param destinationID the id of the contact the user wrote to
|
||||
* @param sourceID the ID of the originator
|
||||
* @param destinationID the ID of the contact the user wrote to
|
||||
* @since Envoy Common v0.2-beta
|
||||
*/
|
||||
public IsTyping(Long sourceID, long destinationID) {
|
||||
@ -38,7 +35,7 @@ public final class IsTyping extends Event<Long> {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the id of the contact in whose chat the user typed something
|
||||
* @return the ID of the contact in whose chat the user typed something
|
||||
* @since Envoy Common v0.2-beta
|
||||
*/
|
||||
public long getDestinationID() { return destinationID; }
|
||||
|
@ -2,11 +2,7 @@ package envoy.event;
|
||||
|
||||
/**
|
||||
* This class allows envoy users to send an issue proposal to the server who, if
|
||||
* not disabled by its admin, will forward it directly to gitea.
|
||||
* <p>
|
||||
* Project: <strong>common</strong><br>
|
||||
* File: <strong>IssueProposal.java</strong><br>
|
||||
* Created: <strong>05.08.2020</strong><br>
|
||||
* not disabled by its administrator, will forward it directly to Gitea.
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Common v0.2-beta
|
||||
|
@ -5,10 +5,6 @@ import java.time.Instant;
|
||||
import envoy.data.Message;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-common</strong><br>
|
||||
* File: <strong>MessageStatusChange.java</strong><br>
|
||||
* Created: <strong>6 Jan 2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Common v0.2-alpha
|
||||
*/
|
||||
|
@ -3,14 +3,11 @@ package envoy.event;
|
||||
import envoy.data.Contact;
|
||||
|
||||
/**
|
||||
* This event informs<br>
|
||||
* This event informs
|
||||
* <p>
|
||||
* a) the server of the name change of a user or a group.
|
||||
* b) another user of this users name change.
|
||||
*
|
||||
* Project: <strong>envoy-common</strong><br>
|
||||
* File: <strong>NameChange.java</strong><br>
|
||||
* Created: <strong>25 Mar 2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Common v0.1-beta
|
||||
*/
|
||||
|
@ -2,10 +2,6 @@ package envoy.event;
|
||||
|
||||
/**
|
||||
* This event can be used to transmit a new authentication token to a client.
|
||||
* <p>
|
||||
* Project: <strong>envoy-common</strong><br>
|
||||
* File: <strong>NewAuthToken.java</strong><br>
|
||||
* Created: <strong>19.09.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Common v0.2-beta
|
||||
|
@ -3,10 +3,6 @@ package envoy.event;
|
||||
/**
|
||||
* This event is used so that the server can tell the client that attachments
|
||||
* will be filtered out.
|
||||
* <p>
|
||||
* Project: <strong>common</strong><br>
|
||||
* File: <strong>NoAttachments.java</strong><br>
|
||||
* Created: <strong>22.08.2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Common v0.2-beta
|
||||
|
@ -3,10 +3,6 @@ package envoy.event;
|
||||
import envoy.data.Contact;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-common</strong><br>
|
||||
* File: <strong>PasswordChangeRequest.java</strong><br>
|
||||
* Created: <strong>31.07.2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Common v0.2-beta
|
||||
*/
|
||||
|
@ -3,10 +3,6 @@ package envoy.event;
|
||||
/**
|
||||
* This class acts as a notice to the user whether his
|
||||
* {@link envoy.event.PasswordChangeRequest} was successful.
|
||||
* <p>
|
||||
* Project: <strong>envoy-common</strong><br>
|
||||
* File: <strong>PasswordChangeResult.java</strong><br>
|
||||
* Created: <strong>01.08.2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Common v0.2-beta
|
||||
|
@ -1,10 +1,6 @@
|
||||
package envoy.event;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-common</strong><br>
|
||||
* File: <strong>ProfilePicChange.java</strong><br>
|
||||
* Created: <strong>31.07.2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Common v0.2-beta
|
||||
*/
|
||||
|
@ -4,10 +4,6 @@ import envoy.data.User;
|
||||
import envoy.data.User.UserStatus;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-common</strong><br>
|
||||
* File: <strong>UserStatusChange.java</strong><br>
|
||||
* Created: <strong>1 Feb 2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Common v0.2-alpha
|
||||
*/
|
||||
|
@ -1,15 +1,10 @@
|
||||
package envoy.event.contact;
|
||||
|
||||
import envoy.data.Contact;
|
||||
import envoy.event.ElementOperation;
|
||||
import envoy.event.Event;
|
||||
import envoy.event.*;
|
||||
|
||||
/**
|
||||
* Signifies the modification of a contact list.<br>
|
||||
* <br>
|
||||
* Project: <strong>envoy-common</strong><br>
|
||||
* File: <strong>ContactOperation.java</strong><br>
|
||||
* Created: <strong>05.02.2020</strong><br>
|
||||
* Signifies the modification of a contact list.
|
||||
*
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy Common v0.2-alpha
|
||||
|
@ -4,10 +4,6 @@ import envoy.event.Event;
|
||||
|
||||
/**
|
||||
* Requests a user search from the server.
|
||||
* <p>
|
||||
* Project: <strong>envoy-common</strong><br>
|
||||
* File: <strong>UserSearchRequest.java</strong><br>
|
||||
* Created: <strong>05.02.2020</strong><br>
|
||||
*
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy Common v0.2-alpha
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user