Add Ability to Logout #50

Merged
kske merged 7 commits from f/logout into develop 2020-09-27 15:48:13 +02:00
18 changed files with 378 additions and 55 deletions

View File

@ -56,4 +56,11 @@ public final class Cache<T> implements Consumer<T>, Serializable {
elements.forEach(processor::accept);
elements.clear();
}
/**
* Clears this cache of all stored elements.
*
* @since Envoy Client v0.2-beta
*/
public void clear() { elements.clear(); }
}

View File

@ -6,7 +6,7 @@ import java.util.*;
/**
* Stores a heterogeneous map of {@link Cache} objects with different type
* parameters.
*
*
* @author Kai S. K. Engelbart
* @since Envoy Client v0.1-beta
*/
@ -18,7 +18,7 @@ public final class CacheMap implements Serializable {
/**
* Adds a cache to the map.
*
*
* @param <T> the type accepted by the cache
* @param key the class that maps to the cache
* @param cache the cache to store
@ -28,7 +28,7 @@ public final class CacheMap implements Serializable {
/**
* Returns a cache mapped by a class.
*
*
* @param <T> the type accepted by the cache
* @param key the class that maps to the cache
* @return the cache
@ -38,7 +38,7 @@ public final class CacheMap implements Serializable {
/**
* Returns a cache mapped by a class or any of its subclasses.
*
*
* @param <T> the type accepted by the cache
* @param key the class that maps to the cache
* @return the cache
@ -47,7 +47,7 @@ public final class CacheMap implements Serializable {
public <T> Cache<? super T> getApplicable(Class<T> key) {
Cache<? super T> cache = get(key);
if (cache == null)
for (var e : map.entrySet())
for (final var e : map.entrySet())
if (e.getKey().isAssignableFrom(key))
cache = (Cache<? super T>) e.getValue();
return cache;
@ -58,4 +58,11 @@ public final class CacheMap implements Serializable {
* @since Envoy Client v0.1-beta
*/
public Map<Class<?>, Cache<?>> getMap() { return map; }
/**
* Clears the caches of this map of any values.
*
* @since Envoy Client v0.2-beta
*/
public void clear() { map.values().forEach(Cache::clear); }
}

View File

@ -9,7 +9,7 @@ import java.util.logging.*;
import javafx.collections.*;
import envoy.client.event.EnvoyCloseEvent;
import envoy.client.event.*;
import envoy.data.*;
import envoy.data.Message.MessageStatus;
import envoy.event.*;
@ -39,6 +39,10 @@ public final class LocalDB implements EventListener {
private CacheMap cacheMap = new CacheMap();
private String authToken;
// Auto save timer
private Timer autoSaver;
Outdated
Review

This should be // Auto save time.

This should be `// Auto save time`.
Outdated
Review

you mean // Auto save timer?

you mean `// Auto save timer`?
private boolean autoSaveRestart = true;
// State management
private Instant lastSync = Instant.EPOCH;
@ -168,7 +172,14 @@ public final class LocalDB implements EventListener {
* @since Envoy Client v0.2-beta
*/
public void initAutoSave() {
new Timer("LocalDB Autosave", true).schedule(new TimerTask() {
// A logout happened so the timer should be restarted
delvh marked this conversation as resolved Outdated
Outdated
Review

Maybe move the comment above the if statement.

Maybe move the comment above the `if` statement.
if (autoSaveRestart) {
autoSaver = new Timer("LocalDB Autosave", true);
autoSaveRestart = false;
}
autoSaver.schedule(new TimerTask() {
@Override
public void run() { save(); }
@ -244,6 +255,24 @@ public final class LocalDB implements EventListener {
@Event
private void onNewAuthToken(NewAuthToken evt) { authToken = evt.get(); }
/**
* Deletes all associations to the current user.
*
* @since Envoy Client v0.2-beta
*/
@Event(eventType = Logout.class, priority = 100)
private void onLogout() {
autoSaver.cancel();
autoSaveRestart = true;
lastLoginFile.delete();
userFile = null;
user = null;
authToken = null;
chats.clear();
lastSync = Instant.EPOCH;
cacheMap.clear();
}
/**
* @return a {@code Map<String, User>} of all users stored locally with their
* user names as keys

View File

@ -88,6 +88,8 @@ public final class Settings implements EventListener {
new SettingsItem<>(new File(System.getProperty("user.home") + "/Downloads/"), "Download location",
"The location where files will be saved to"));
items.putIfAbsent("autoSaveDownloads", new SettingsItem<>(false, "Save without asking?", "Should downloads be saved without asking?"));
items.putIfAbsent("askForConfirmation",
new SettingsItem<>(true, "Ask for confirmation", "Will ask for confirmation before doing certain things"));
}
/**
@ -178,6 +180,25 @@ public final class Settings implements EventListener {
*/
public void setHideOnClose(boolean hideOnClose) { ((SettingsItem<Boolean>) items.get("hideOnClose")).set(hideOnClose); }
/**
* @return whether a confirmation dialog should be displayed before certain
* actions
* @since Envoy Client v0.2-alpha
*/
public Boolean isAskForConfirmation() { return (Boolean) items.get("askForConfirmation").get(); }
/**
* Changes the behavior of calling certain functionality by displaying a
* confirmation dialog before executing it.
*
* @param askForConfirmation whether confirmation dialogs should be displayed
* before certain actions
* @since Envoy Client v0.2-alpha
*/
public void setAskForConfirmation(boolean askForConfirmation) {
((SettingsItem<Boolean>) items.get("askForConfirmation")).set(askForConfirmation);
}
/**
* @return the items
*/

View File

@ -17,6 +17,22 @@ public final class SystemCommandBuilder {
private String description;
private int relevance;
private final SystemCommandMap commandsMap;
/**
* Creates a new {@code SystemCommandsBuilder} without underlying
* {@link SystemCommandMap}.
*
* @since Envoy Client v0.2-beta
*/
public SystemCommandBuilder() { this(null); }
/**
* @param commandsMap the map to use when calling build (optional)
* @since Envoy Client v0.2-beta
*/
public SystemCommandBuilder(SystemCommandMap commandsMap) { this.commandsMap = commandsMap; }
/**
* @param numberOfArguments the numberOfArguments to set
* @return this {@code SystemCommandBuilder}
@ -120,6 +136,7 @@ public final class SystemCommandBuilder {
/**
* Builds a {@code SystemCommand} based upon the previously entered data.<br>
* Automatically adds the built object to the given map.
* At the end, this {@code SystemCommandBuilder} <b>can</b> be reset but must
* not be.
*
@ -136,4 +153,78 @@ public final class SystemCommandBuilder {
if (reset) reset();
return sc;
}
/**
* Builds a {@code SystemCommand} based upon the previously entered data.
* Automatically adds the built object to the given map.
*
* @param command the command under which to store the SystemCommand in the
* {@link SystemCommandMap}
* @return the built {@code SystemCommand}
* @throws NullPointerException if no map has been assigned to this builder
* @since Envoy Client v0.2-beta
*/
public SystemCommand build(String command) { return build(command, true); }
/**
* Builds a {@code SystemCommand} based upon the previously entered data.<br>
* Automatically adds the built object to the given map.
* {@code SystemCommand#numberOfArguments} will be set to 0, regardless of the
* previous value.<br>
* At the end, this {@code SystemCommandBuilder} will be reset.
*
* @param command the command under which to store the SystemCommand in the
* {@link SystemCommandMap}
* @return the built {@code SystemCommand}
* @throws NullPointerException if no map has been assigned to this builder
* @since Envoy Client v0.2-beta
*/
public SystemCommand buildNoArg(String command) {
numberOfArguments = 0;
return build(command, true);
}
/**
* Builds a {@code SystemCommand} based upon the previously entered data.<br>
* Automatically adds the built object to the given map.
* {@code SystemCommand#numberOfArguments} will be set to use the rest of the
* string as argument, regardless of the previous value.<br>
* At the end, this {@code SystemCommandBuilder} will be reset.
*
* @param command the command under which to store the SystemCommand in the
* {@link SystemCommandMap}
* @return the built {@code SystemCommand}
* @throws NullPointerException if no map has been assigned to this builder
* @since Envoy Client v0.2-beta
*/
public SystemCommand buildRemainingArg(String command) {
numberOfArguments = -1;
return build(command, true);
}
/**
* Builds a {@code SystemCommand} based upon the previously entered data.<br>
* Automatically adds the built object to the given map.
* At the end, this {@code SystemCommandBuilder} <b>can</b> be reset but must
* not be.
*
* @param command the command under which to store the SystemCommand in the
* {@link SystemCommandMap}
* @param reset whether this {@code SystemCommandBuilder} should be reset
* afterwards.<br>
* This can be useful if another command wants to execute
* something
* similar
* @return the built {@code SystemCommand}
* @throws NullPointerException if no map has been assigned to this builder
* @since Envoy Client v0.2-beta
*/
public SystemCommand build(String command, boolean reset) {
final var sc = new SystemCommand(action, numberOfArguments, defaults, description);
sc.setRelevance(relevance);
if (commandsMap != null) commandsMap.add(command, sc);
else throw new NullPointerException("No map in SystemCommandsBuilder present");
if (reset) reset();
return sc;
}
}

View File

@ -14,13 +14,13 @@ import envoy.util.EnvoyLog;
* @author Leon Hofmeister
* @since Envoy Client v0.2-beta
*/
public final class SystemCommandsMap {
public final class SystemCommandMap {
private final Map<String, SystemCommand> systemCommands = new HashMap<>();
private final Pattern commandPattern = Pattern.compile("^[a-zA-Z0-9_:!\\(\\)\\?\\.\\,\\;\\-]+$");
private static final Logger logger = EnvoyLog.getLogger(SystemCommandsMap.class);
private static final Logger logger = EnvoyLog.getLogger(SystemCommandMap.class);
/**
* Adds a new command to the map if the command name is valid.
@ -29,7 +29,7 @@ public final class SystemCommandsMap {
* given action
* @param systemCommand the command to add - can be built using
* {@link SystemCommandBuilder}
* @see SystemCommandsMap#isValidKey(String)
* @see SystemCommandMap#isValidKey(String)
* @since Envoy Client v0.2-beta
*/
public void add(String command, SystemCommand systemCommand) {
@ -44,7 +44,7 @@ public final class SystemCommandsMap {
* map).
* <p>
* Usage example:<br>
* {@code SystemCommandsMap systemCommands = new SystemCommandsMap();}<br>
* {@code SystemCommandMap systemCommands = new SystemCommandMap();}<br>
* {@code Button button = new Button();}
* {@code systemCommands.add("example", text -> button.setText(text.get(0), 1);}<br>
* {@code ....}<br>
@ -128,7 +128,7 @@ public final class SystemCommandsMap {
* map).
* <p>
* Usage example:<br>
* {@code SystemCommandsMap systemCommands = new SystemCommandsMap();}<br>
* {@code SystemCommandMap systemCommands = new SystemCommandMap();}<br>
* {@code Button button = new Button();}<br>
* {@code systemCommands.add("example", (words)-> button.setText(words.get(0), 1);}<br>
* {@code ....}<br>

View File

@ -0,0 +1,14 @@
package envoy.client.event;
import envoy.event.Event.Valueless;
/**
* Indicates that a logout has been requested.
*
* @author Leon Hofmeister
delvh marked this conversation as resolved Outdated
Outdated
Review

Fix your @author tag.

Fix your `@author` tag.
* @since Envoy Client v0.2-beta
*/
public final class Logout extends Valueless {
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,36 @@
package envoy.client.helper;
import javafx.scene.control.*;
import envoy.client.data.Settings;
/**
* Provides methods that are commonly used for alerts.
*
* @author Leon Hofmeister
* @since Envoy Client v0.2-beta
*/
public final class AlertHelper {
delvh marked this conversation as resolved Outdated
Outdated
Review

Make this final.

Make this `final`.
private AlertHelper() {}
/**
* Asks for a confirmation dialog if {@link Settings#isAskForConfirmation()}
* returns {@code true}.
* Immediately executes the action if no dialog was requested or the dialog was
* exited with a confirmation.
* Does nothing if the dialog was closed without clicking on OK.
*
* @param alert the (customized) alert to show. <strong>Should not be shown
* already</strong>
* @param action the action to perform in case of success
* @since Envoy Client v0.2-beta
*/
public static void confirmAction(Alert alert, Runnable action) {
alert.setHeight(225);
alert.setWidth(400);
alert.setHeaderText("");
if (Settings.getInstance().isAskForConfirmation()) alert.showAndWait().filter(ButtonType.OK::equals).ifPresent(bu -> action.run());
else action.run();
}
}

View File

@ -0,0 +1,57 @@
package envoy.client.helper;
import java.util.logging.Level;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import envoy.client.data.*;
import envoy.client.event.*;
import envoy.client.ui.SceneContext.SceneInfo;
import envoy.util.EnvoyLog;
import dev.kske.eventbus.EventBus;
/**
* Simplifies shutdown actions.
Outdated
Review

What on earth does that even mean?

What on earth does that even mean?
Outdated
Review

Changed it now.
If you don't like it, suggest a better alternative.
I cannot think of one.

Changed it now. If you don't like it, suggest a better alternative. I cannot think of one.
*
* @author Leon Hofmeister
* @since Envoy Client v0.2-beta
*/
public final class ShutdownHelper {
delvh marked this conversation as resolved Outdated
Outdated
Review

Make this final.

Make this `final`.
private ShutdownHelper() {}
/**
* Exits Envoy or minimizes it, depending on the current state of
* {@link Settings#isHideOnClose()}.
*
* @since Envoy Client v0.2-beta
*/
public static void exit() {
if (Settings.getInstance().isHideOnClose()) Context.getInstance().getStage().setIconified(true);
else {
EventBus.getInstance().dispatch(new EnvoyCloseEvent());
System.exit(0);
}
}
/**
* Logs the current user out and reopens
* {@link envoy.client.ui.controller.LoginScene}.
*
* @since Envoy Client v0.2-beta
*/
public static void logout() {
final var alert = new Alert(AlertType.CONFIRMATION);
alert.setTitle("Logout?");
alert.setContentText("Are you sure you want to log out?");
AlertHelper.confirmAction(alert, () -> {
EnvoyLog.getLogger(ShutdownHelper.class).log(Level.INFO, "A logout was requested");
EventBus.getInstance().dispatch(new EnvoyCloseEvent());
EventBus.getInstance().dispatch(new Logout());
Context.getInstance().getSceneContext().load(SceneInfo.LOGIN_SCENE);
});
}
}

View File

@ -0,0 +1,9 @@
/**
* Provides helper methods that reduce boilerplate code.
*
* @author Leon Hofmeister
* @author Kai S. K. Engelbert
* @author Maximilian K&auml;fer
* @since Envoy Client v0.2-beta
*/
package envoy.client.helper;

View File

@ -100,7 +100,6 @@ public final class Client implements EventListener, Closeable {
}
online = true;
logger.log(Level.INFO, "Handshake completed.");
}
@ -183,8 +182,14 @@ public final class Client implements EventListener, Closeable {
if (online) {
logger.log(Level.INFO, "Closing connection...");
try {
// The sender must be reset as otherwise the handshake is immediately closed
sender = null;
online = false;
socket.close();
} catch (final IOException e) {}
} catch (final IOException e) {
logger.log(Level.WARNING, "Failed to close socket: ", e);
}
}
}
@ -212,6 +217,7 @@ public final class Client implements EventListener, Closeable {
/**
* @return the {@link Receiver} used by this {@link Client}
* @since v0.2-alpha
*/
public Receiver getReceiver() { return receiver; }

View File

@ -36,6 +36,7 @@ public final class Receiver extends Thread {
public Receiver(InputStream in) {
super("Receiver");
this.in = in;
setDaemon(true);
}
/**

View File

@ -12,6 +12,7 @@ import javafx.stage.Stage;
import envoy.client.data.Settings;
import envoy.client.event.*;
import envoy.client.helper.ShutdownHelper;
import envoy.util.EnvoyLog;
import dev.kske.eventbus.*;
@ -92,6 +93,7 @@ public final class SceneContext implements EventListener {
* @since Envoy Client v0.1-beta
*/
public void load(SceneInfo sceneInfo) {
EnvoyLog.getLogger(SceneContext.class).log(Level.FINER, "Loading scene " + sceneInfo);
loader.setRoot(null);
loader.setController(null);
@ -103,18 +105,17 @@ public final class SceneContext implements EventListener {
sceneStack.push(scene);
stage.setScene(scene);
// Adding the option to exit Linux-like with "Control" + "Q"
scene.getAccelerators()
.put(new KeyCodeCombination(KeyCode.Q, KeyCombination.CONTROL_DOWN),
() -> {
// Presumably no Settings are loaded in the login scene, hence Envoy is closed
// directly
if (sceneInfo != SceneInfo.LOGIN_SCENE && settings.isHideOnClose()) stage.setIconified(true);
else {
EventBus.getInstance().dispatch(new EnvoyCloseEvent());
System.exit(0);
}
});
// Add the option to exit Linux-like with "Control" + "Q"
scene.getAccelerators().put(new KeyCodeCombination(KeyCode.Q, KeyCombination.CONTROL_DOWN), ShutdownHelper::exit);
// Add the option to logout using "Control"+"Shift"+"L" if not in login scene
if (sceneInfo != SceneInfo.LOGIN_SCENE) scene.getAccelerators()
.put(new KeyCodeCombination(KeyCode.L, KeyCombination.CONTROL_DOWN, KeyCombination.SHIFT_DOWN), ShutdownHelper::logout);
// Add the option to open the settings scene with "Control"+"S", if being in
// chat scene
if (sceneInfo.equals(SceneInfo.CHAT_SCENE))
scene.getAccelerators().put(new KeyCodeCombination(KeyCode.S, KeyCombination.CONTROL_DOWN), () -> load(SceneInfo.SETTINGS_SCENE));
// The LoginScene is the only scene not intended to be resized
// As strange as it seems, this is needed as otherwise the LoginScene won't be
@ -163,6 +164,12 @@ public final class SceneContext implements EventListener {
}
}
@Event(eventType = Logout.class, priority = 150)
private void onLogout() {
sceneStack.clear();
controllerStack.clear();
}
@Event(priority = 150, eventType = ThemeChangeEvent.class)
private void onThemeChange() { applyCSS(); }

View File

@ -11,7 +11,7 @@ import javafx.scene.control.Alert.AlertType;
import javafx.stage.Stage;
import envoy.client.data.*;
import envoy.client.event.EnvoyCloseEvent;
import envoy.client.helper.ShutdownHelper;
import envoy.client.net.Client;
import envoy.client.ui.SceneContext.SceneInfo;
import envoy.client.ui.controller.LoginScene;
@ -22,10 +22,8 @@ import envoy.event.*;
import envoy.exception.EnvoyException;
import envoy.util.EnvoyLog;
import dev.kske.eventbus.EventBus;
/**
* Handles application startup and shutdown.
* Handles application startup.
*
* @author Kai S. K. Engelbart
* @author Maximilian K&auml;fer
@ -55,6 +53,8 @@ public final class Startup extends Application {
*/
@Override
public void start(Stage stage) throws Exception {
// Initialize config and logger
try {
config.loadAll(Startup.class, "client.properties", getParameters().getRaw().toArray(new String[0]));
EnvoyLog.initialize(config);
@ -67,7 +67,7 @@ public final class Startup extends Application {
// Initialize the local database
try {
var localDBFile = new File(config.getHomeDirectory(), config.getServer());
final var localDBFile = new File(config.getHomeDirectory(), config.getServer());
logger.info("Initializing LocalDB at " + localDBFile);
localDB = new LocalDB(localDBFile);
} catch (IOException | EnvoyException e) {
@ -114,7 +114,6 @@ public final class Startup extends Application {
cacheMap.put(MessageStatusChange.class, new Cache<MessageStatusChange>());
cacheMap.put(GroupMessageStatusChange.class, new Cache<GroupMessageStatusChange>());
try {
final var client = context.getClient();
client.performHandshake(credentials, cacheMap);
if (client.isOnline()) {
loadChatScene();
@ -208,13 +207,8 @@ public final class Startup extends Application {
if (StatusTrayIcon.isSupported()) {
// Configure hide on close
stage.setOnCloseRequest(e -> {
if (Settings.getInstance().isHideOnClose()) {
stage.setIconified(true);
e.consume();
} else EventBus.getInstance().dispatch(new EnvoyCloseEvent());
});
// Exit or minimize the stage when a close request occurs
stage.setOnCloseRequest(e -> { ShutdownHelper.exit(); if (Settings.getInstance().isHideOnClose()) e.consume(); });
// Initialize status tray icon
final var trayIcon = new StatusTrayIcon(stage);

View File

@ -6,6 +6,7 @@ import java.awt.TrayIcon.MessageType;
import javafx.application.Platform;
import javafx.stage.Stage;
import envoy.client.helper.ShutdownHelper;
import envoy.client.util.IconUtil;
import envoy.data.Message;
@ -53,7 +54,7 @@ public final class StatusTrayIcon implements EventListener {
final PopupMenu popup = new PopupMenu();
final MenuItem exitMenuItem = new MenuItem("Exit");
exitMenuItem.addActionListener(evt -> { Platform.exit(); System.exit(0); });
exitMenuItem.addActionListener(evt -> ShutdownHelper.exit());
popup.add(exitMenuItem);
trayIcon.setPopupMenu(popup);

View File

@ -28,8 +28,10 @@ import envoy.client.data.*;
import envoy.client.data.audio.AudioRecorder;
import envoy.client.data.commands.*;
import envoy.client.event.*;
import envoy.client.helper.ShutdownHelper;
import envoy.client.net.*;
import envoy.client.ui.*;
import envoy.client.ui.SceneContext.SceneInfo;
import envoy.client.ui.control.*;
import envoy.client.ui.listcell.*;
import envoy.client.util.*;
@ -142,7 +144,7 @@ public final class ChatScene implements EventListener, Restorable {
private final WriteProxy writeProxy = context.getWriteProxy();
private final SceneContext sceneContext = context.getSceneContext();
private final AudioRecorder recorder = new AudioRecorder();
private final SystemCommandsMap messageTextAreaCommands = new SystemCommandsMap();
private final SystemCommandMap messageTextAreaCommands = new SystemCommandMap();
private final Tooltip onlyIfOnlineTooltip = new Tooltip("You need to be online to do this");
private static Image DEFAULT_ATTACHMENT_VIEW_IMAGE = IconUtil.loadIconThemeSensitive("attachment_present", 20);
@ -303,20 +305,37 @@ public final class ChatScene implements EventListener, Restorable {
else recipientProfilePic.setImage(IconUtil.loadIconThemeSensitive("group_icon", 43));
}
@Event(eventType = Logout.class, priority = 200)
private void onLogout() { eventBus.removeListener(this); }
/**
* Initializes all {@code SystemCommands} used in {@code ChatScene}.
*
* @since Envoy Client v0.2-beta
*/
private void initializeSystemCommandsMap() {
final var builder = new SystemCommandBuilder();
final var builder = new SystemCommandBuilder(messageTextAreaCommands);
// Do A Barrel roll initialization
final var random = new Random();
builder.setAction(text -> doABarrelRoll(Integer.parseInt(text.get(0)), Double.parseDouble(text.get(1))))
.setDefaults(Integer.toString(random.nextInt(3) + 1), Double.toString(random.nextDouble() * 3 + 1))
.setDescription("See for yourself :)")
.setNumberOfArguments(2);
messageTextAreaCommands.add("DABR", builder.build());
.setNumberOfArguments(2)
.build("dabr");
// Logout initialization
builder.setAction(text -> ShutdownHelper.logout()).setNumberOfArguments(0).setDescription("Logs you out.").build("logout");
// Exit initialization
builder.setAction(text -> ShutdownHelper.exit()).setNumberOfArguments(0).setDescription("Exits the program").build("exit", false);
builder.build("q");
// Open settings scene initialization
builder.setAction(text -> sceneContext.load(SceneInfo.SETTINGS_SCENE))
.setNumberOfArguments(0)
.setDescription("Opens the settings screen")
.build("settings");
}
@Override

View File

@ -9,6 +9,7 @@ import javafx.scene.control.Alert.AlertType;
import envoy.client.data.Context;
import envoy.client.event.BackEvent;
import envoy.client.helper.AlertHelper;
import envoy.client.net.Client;
import envoy.client.ui.control.ContactControl;
import envoy.client.ui.listcell.ListCellFactory;
@ -53,6 +54,7 @@ public class ContactSearchTab implements EventListener {
private void initialize() {
eventBus.registerListener(this);
userList.setCellFactory(new ListCellFactory<>(ContactControl::new));
alert.setTitle("Add User?");
}
@Event
@ -104,16 +106,23 @@ public class ContactSearchTab implements EventListener {
final var user = userList.getSelectionModel().getSelectedItem();
if (user != null) {
currentlySelectedUser = user;
final var event = new ContactOperation(currentlySelectedUser, ElementOperation.ADD);
// Sends the event to the server
client.send(event);
// Removes the chosen user and updates the UI
userList.getItems().remove(currentlySelectedUser);
eventBus.dispatch(event);
logger.log(Level.INFO, "Added user " + currentlySelectedUser);
alert.setContentText("Add user " + currentlySelectedUser.getName() + " to your contacts?");
AlertHelper.confirmAction(alert, this::addAsContact);
}
}
private void addAsContact() {
// Sends the event to the server
final var event = new ContactOperation(currentlySelectedUser, ElementOperation.ADD);
client.send(event);
// Removes the chosen user and updates the UI
userList.getItems().remove(currentlySelectedUser);
eventBus.dispatch(event);
logger.log(Level.INFO, "Added user " + currentlySelectedUser);
}
@FXML
private void backButtonClicked() { eventBus.dispatch(new BackEvent()); }
}

View File

@ -4,6 +4,7 @@ import javafx.scene.control.*;
import envoy.client.data.SettingsItem;
import envoy.client.event.ThemeChangeEvent;
import envoy.client.helper.ShutdownHelper;
import envoy.data.User.UserStatus;
import dev.kske.eventbus.EventBus;
@ -24,23 +25,30 @@ public final class GeneralSettingsPane extends SettingsPane {
// TODO: Support other value types
final var settingsItems = settings.getItems();
final var hideOnCloseCheckbox = new SettingsCheckbox((SettingsItem<Boolean>) settingsItems.get("hideOnClose"));
hideOnCloseCheckbox.setTooltip(new Tooltip("If selected, Envoy will still be present in the task bar when closed."));
final var hideOnCloseTooltip = new Tooltip("If selected, Envoy will still be present in the task bar when closed.");
hideOnCloseTooltip.setWrapText(true);
hideOnCloseCheckbox.setTooltip(hideOnCloseTooltip);
getChildren().add(hideOnCloseCheckbox);
final var enterToSendCheckbox = new SettingsCheckbox((SettingsItem<Boolean>) settingsItems.get("enterToSend"));
final var enterToSendTooltip = new Tooltip(
"If selected, messages can be sent pressing \"Enter\". They can always be sent by pressing \"Ctrl\" + \"Enter\"");
"When selected, messages can be sent pressing \"Enter\". A line break can be inserted by pressing \"Ctrl\" + \"Enter\". Else it will be the other way around.");
enterToSendTooltip.setWrapText(true);
enterToSendCheckbox.setTooltip(enterToSendTooltip);
getChildren().add(enterToSendCheckbox);
final var askForConfirmationCheckbox = new SettingsCheckbox((SettingsItem<Boolean>) settingsItems.get("askForConfirmation"));
final var askForConfirmationTooltip = new Tooltip("When selected, nothing will prompt a confirmation dialog");
askForConfirmationTooltip.setWrapText(true);
askForConfirmationCheckbox.setTooltip(askForConfirmationTooltip);
getChildren().add(askForConfirmationCheckbox);
final var combobox = new ComboBox<String>();
combobox.getItems().add("dark");
combobox.getItems().add("light");
combobox.setTooltip(new Tooltip("Determines the current theme Envoy will be displayed in."));
combobox.setValue(settings.getCurrentTheme());
combobox.setOnAction(
e -> { settings.setCurrentTheme(combobox.getValue()); EventBus.getInstance().dispatch(new ThemeChangeEvent()); });
combobox.setOnAction(e -> { settings.setCurrentTheme(combobox.getValue()); EventBus.getInstance().dispatch(new ThemeChangeEvent()); });
getChildren().add(combobox);
final var statusComboBox = new ComboBox<UserStatus>();
@ -50,5 +58,12 @@ public final class GeneralSettingsPane extends SettingsPane {
// TODO add action when value is changed
statusComboBox.setOnAction(e -> {});
getChildren().add(statusComboBox);
final var logoutButton = new Button("Logout");
logoutButton.setOnAction(e -> ShutdownHelper.logout());
final var logoutTooltip = new Tooltip("Brings you back to the login screen and removes \"remember me\" status from this account");
logoutTooltip.setWrapText(true);
logoutButton.setTooltip(logoutTooltip);
getChildren().add(logoutButton);
}
}