Add Ability to Change the User Status Freely #90

Merged
delvh merged 7 commits from f/change-user-status into develop 2020-10-10 14:16:29 +02:00
6 changed files with 64 additions and 25 deletions
Showing only changes of commit 3c8c544cbd - Show all commits

View File

@ -27,8 +27,6 @@ public final class AlertHelper {
* @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

@ -14,6 +14,7 @@ import envoy.client.data.Settings;
import envoy.client.event.*;
import envoy.client.helper.ShutdownHelper;
import envoy.client.util.UserUtil;
import envoy.data.User.UserStatus;
import envoy.util.EnvoyLog;
import dev.kske.eventbus.*;
@ -127,10 +128,23 @@ public final class SceneContext implements EventListener {
// Add the option to exit Linux-like with "Control" + "Q" or "Alt" + "F4"
accelerators.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)
if (sceneInfo != SceneInfo.LOGIN_SCENE) {
kske marked this conversation as resolved
Review
  1. Are these key combinations documented anywhere?
  2. Can we stop putting scene specific code inside SceneContext, that could be completely generic as to what scenes it displays, except for being littered with code specific to LoginScene which nobody would expect here?

(yes, these are both rhetorical questions)

1. Are these key combinations documented anywhere? 2. Can we stop putting scene specific code inside `SceneContext`, that could be completely generic as to what scenes it displays, except for being littered with code specific to `LoginScene` which nobody would expect here? (yes, these are both rhetorical questions)
Review

to 1. While I know what you mean, I'd still say that this goes beyond the scope of this branch.
to 2. While I know what you mean, I'd still say that this goes beyond the scope of this branch.

It seems as if one of the next branches should cleanup that in SceneContext. I could do it in that branch, but than this branch will have two completely separate tasks on it (and it would be eve larger than it already is).

to 1. While I know what you mean, I'd still say that this goes beyond the scope of this branch. to 2. While I know what you mean, I'd still say that this goes beyond the scope of this branch. It seems as if one of the next branches should cleanup that in SceneContext. I could do it in that branch, but than this branch will have two completely separate tasks on it (and it would be eve larger than it already is).
Review

No, I think you are right. The cleanup should be performed inside a separate branch.

No, I think you are right. The cleanup should be performed inside a separate branch.
Review

See #91

See #91
// Add the option to logout using "Control"+"Shift"+"L"
accelerators.put(new KeyCodeCombination(KeyCode.L, KeyCombination.CONTROL_DOWN, KeyCombination.SHIFT_DOWN), UserUtil::logout);
// Add the option to change status using "Control" + "Shift" +
// (o)F(fline)/ A(way)/ B(usy)/(o)N(line)
accelerators.put(new KeyCodeCombination(KeyCode.F, KeyCombination.CONTROL_DOWN, KeyCombination.SHIFT_DOWN),
() -> UserUtil.changeStatus(UserStatus.OFFLINE));
accelerators.put(new KeyCodeCombination(KeyCode.A, KeyCombination.CONTROL_DOWN, KeyCombination.SHIFT_DOWN),
() -> UserUtil.changeStatus(UserStatus.AWAY));
accelerators.put(new KeyCodeCombination(KeyCode.B, KeyCombination.CONTROL_DOWN, KeyCombination.SHIFT_DOWN),
() -> UserUtil.changeStatus(UserStatus.BUSY));
accelerators.put(new KeyCodeCombination(KeyCode.N, KeyCombination.CONTROL_DOWN, KeyCombination.SHIFT_DOWN),
() -> UserUtil.changeStatus(UserStatus.ONLINE));
}
// Add the option to open the settings scene with "Control"+"S", if being in
// chat scene
if (sceneInfo == SceneInfo.CHAT_SCENE)

View File

@ -7,8 +7,9 @@ import javafx.application.Platform;
import javafx.stage.Stage;
import envoy.client.helper.ShutdownHelper;
import envoy.client.util.IconUtil;
import envoy.client.util.*;
import envoy.data.Message;
import envoy.data.User.UserStatus;
import dev.kske.eventbus.*;
import dev.kske.eventbus.Event;
@ -51,12 +52,27 @@ public final class StatusTrayIcon implements EventListener {
trayIcon.setImageAutoSize(true);
trayIcon.setToolTip("You are notified if you have unread messages.");
final PopupMenu popup = new PopupMenu();
final var popup = new PopupMenu();
final MenuItem exitMenuItem = new MenuItem("Exit");
// Adding the exit menu item
final var exitMenuItem = new MenuItem("Exit");
exitMenuItem.addActionListener(evt -> ShutdownHelper.exit());
popup.add(exitMenuItem);
// Adding the logout menu item
final var logoutMenuItem = new MenuItem("Logout");
logoutMenuItem.addActionListener(evt -> { hide(); UserUtil.logout(); });
popup.add(exitMenuItem);
// Adding the status change items
final var statusSubMenu = new Menu("Change status");
for (final var status : UserStatus.values()) {
final var statusMenuItem = new MenuItem(status.toString().toLowerCase());
statusMenuItem.addActionListener(evt -> UserUtil.changeStatus(status));
statusSubMenu.add(statusMenuItem);
}
popup.add(statusSubMenu);
trayIcon.setPopupMenu(popup);
// Only display messages if the stage is not focused
@ -89,9 +105,8 @@ public final class StatusTrayIcon implements EventListener {
@Event
private void onMessage(Message message) {
if (displayMessages) trayIcon.displayMessage(
message.hasAttachment() ? "New " + message.getAttachment().getType().toString().toLowerCase() + " message received" : "New message received",
message.getText(),
MessageType.INFO);
if (displayMessages) trayIcon
.displayMessage(message.hasAttachment() ? "New " + message.getAttachment().getType().toString().toLowerCase() + " message received"
: "New message received", message.getText(), MessageType.INFO);
}
}

View File

@ -4,7 +4,8 @@ import java.util.Random;
import java.util.function.*;
import java.util.logging.Level;
import javafx.scene.control.ListView;
import javafx.scene.control.*;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.skin.VirtualFlow;
import envoy.client.data.Context;
@ -14,6 +15,7 @@ import envoy.client.ui.SceneContext.SceneInfo;
import envoy.client.ui.controller.ChatScene;
import envoy.client.util.*;
import envoy.data.Message;
import envoy.data.User.UserStatus;
import envoy.util.EnvoyLog;
/**
@ -63,6 +65,17 @@ public final class ChatSceneCommands {
.setDescription("Opens the settings screen")
.buildNoArg("settings");
// Status change initialization
builder.setAction(text -> {
try {
UserUtil.changeStatus(Enum.valueOf(UserStatus.class, text.get(0).toUpperCase()));
} catch (final IllegalArgumentException e) {
final var alert = new Alert(AlertType.ERROR);
kske marked this conversation as resolved
Review

We implement a standardized way of handling system command failures. Wou don't be able to display an alert if Envoy is running on the command line.

We implement a standardized way of handling system command failures. Wou don't be able to display an alert if Envoy is running on the command line.
Review

While I agree with you, there is a reason why I accepted your solution of automatically displaying an alert in case of a system command only with gritted teeth.
For now, this has to suffice. In the future I can well imagine that we can pass a consumer accepting an error to the calling method to handle errors in a command.

But, I'd say this is a branch of its own again and far beyond the scope of this branch.

While I agree with you, there is a reason why I accepted your solution of automatically displaying an alert in case of a system command only with gritted teeth. For now, this has to suffice. In the future I can well imagine that we can pass a consumer accepting an error to the calling method to handle errors in a command. But, I'd say this is a branch of its own again and far beyond the scope of this branch.
Review

I would suggest to introduce a new exception and catch that at a convenient place. No need for consumers.

I however do agree, that this is beyond the scope of this branch.

I would suggest to introduce a new exception and catch that at a convenient place. No need for consumers. I however do agree, that this is beyond the scope of this branch.
alert.setContentText("Please provide an existing status");
alert.showAndWait();
}
}).setDescription("Changes your status to the given status.").setNumberOfArguments(1).setDefaults("OFFLINE").build("status");
// Selection of a new message initialization
messageDependantAction("s",
m -> { messageList.getSelectionModel().clearSelection(); messageList.getSelectionModel().select(m); },

View File

@ -55,18 +55,16 @@ public final class GeneralSettingsPane extends SettingsPane {
combobox.setOnAction(e -> { settings.setCurrentTheme(combobox.getValue()); EventBus.getInstance().dispatch(new ThemeChangeEvent()); });
getChildren().add(combobox);
if (context.getClient().isOnline()) {
final var statusComboBox = new ComboBox<UserStatus>();
statusComboBox.getItems().setAll(UserStatus.values());
statusComboBox.setValue(context.getLocalDB().getUser().getStatus());
statusComboBox.setTooltip(new Tooltip("Change your current status"));
statusComboBox.setOnAction(e -> {
final var status = statusComboBox.getValue();
if (status == null) return;
else UserUtil.changeStatus(status);
});
getChildren().add(statusComboBox);
}
final var statusComboBox = new ComboBox<UserStatus>();
statusComboBox.getItems().setAll(UserStatus.values());
statusComboBox.setValue(context.getLocalDB().getUser().getStatus());
statusComboBox.setTooltip(new Tooltip("Change your current status"));
statusComboBox.setOnAction(e -> {
final var status = statusComboBox.getValue();
if (status == null) return;
else UserUtil.changeStatus(status);
});
getChildren().add(statusComboBox);
final var logoutButton = new Button("Logout");
logoutButton.setOnAction(e -> UserUtil.logout());

View File

@ -53,6 +53,7 @@ public final class UserUtil {
*/
public static void changeStatus(UserStatus newStatus) {
EventBus.getInstance().dispatch(new OwnStatusChange(newStatus));
Context.getInstance().getClient().send(new UserStatusChange(Context.getInstance().getLocalDB().getUser().getID(), newStatus));
if (Context.getInstance().getClient().isOnline())
Context.getInstance().getClient().send(new UserStatusChange(Context.getInstance().getLocalDB().getUser().getID(), newStatus));
}
}