Add Ability to Change the User Status Freely #90
@ -27,8 +27,6 @@ public final class AlertHelper {
|
|||||||
* @since Envoy Client v0.2-beta
|
* @since Envoy Client v0.2-beta
|
||||||
*/
|
*/
|
||||||
public static void confirmAction(Alert alert, Runnable action) {
|
public static void confirmAction(Alert alert, Runnable action) {
|
||||||
alert.setHeight(225);
|
|
||||||
alert.setWidth(400);
|
|
||||||
alert.setHeaderText("");
|
alert.setHeaderText("");
|
||||||
if (Settings.getInstance().isAskForConfirmation()) alert.showAndWait().filter(ButtonType.OK::equals).ifPresent(bu -> action.run());
|
if (Settings.getInstance().isAskForConfirmation()) alert.showAndWait().filter(ButtonType.OK::equals).ifPresent(bu -> action.run());
|
||||||
else action.run();
|
else action.run();
|
||||||
|
@ -14,6 +14,7 @@ import envoy.client.data.Settings;
|
|||||||
import envoy.client.event.*;
|
import envoy.client.event.*;
|
||||||
import envoy.client.helper.ShutdownHelper;
|
import envoy.client.helper.ShutdownHelper;
|
||||||
import envoy.client.util.UserUtil;
|
import envoy.client.util.UserUtil;
|
||||||
|
import envoy.data.User.UserStatus;
|
||||||
import envoy.util.EnvoyLog;
|
import envoy.util.EnvoyLog;
|
||||||
|
|
||||||
import dev.kske.eventbus.*;
|
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"
|
// Add the option to exit Linux-like with "Control" + "Q" or "Alt" + "F4"
|
||||||
accelerators.put(new KeyCodeCombination(KeyCode.Q, KeyCombination.CONTROL_DOWN), ShutdownHelper::exit);
|
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) {
|
||||||
kske marked this conversation as resolved
|
|||||||
if (sceneInfo != SceneInfo.LOGIN_SCENE)
|
|
||||||
|
// Add the option to logout using "Control"+"Shift"+"L"
|
||||||
accelerators.put(new KeyCodeCombination(KeyCode.L, KeyCombination.CONTROL_DOWN, KeyCombination.SHIFT_DOWN), UserUtil::logout);
|
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
|
// Add the option to open the settings scene with "Control"+"S", if being in
|
||||||
// chat scene
|
// chat scene
|
||||||
if (sceneInfo == SceneInfo.CHAT_SCENE)
|
if (sceneInfo == SceneInfo.CHAT_SCENE)
|
||||||
|
@ -7,8 +7,9 @@ import javafx.application.Platform;
|
|||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
import envoy.client.helper.ShutdownHelper;
|
import envoy.client.helper.ShutdownHelper;
|
||||||
import envoy.client.util.IconUtil;
|
import envoy.client.util.*;
|
||||||
import envoy.data.Message;
|
import envoy.data.Message;
|
||||||
|
import envoy.data.User.UserStatus;
|
||||||
|
|
||||||
import dev.kske.eventbus.*;
|
import dev.kske.eventbus.*;
|
||||||
import dev.kske.eventbus.Event;
|
import dev.kske.eventbus.Event;
|
||||||
@ -51,12 +52,27 @@ public final class StatusTrayIcon implements EventListener {
|
|||||||
trayIcon.setImageAutoSize(true);
|
trayIcon.setImageAutoSize(true);
|
||||||
trayIcon.setToolTip("You are notified if you have unread messages.");
|
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());
|
exitMenuItem.addActionListener(evt -> ShutdownHelper.exit());
|
||||||
popup.add(exitMenuItem);
|
popup.add(exitMenuItem);
|
||||||
|
|
||||||
|
// Adding the logout menu item
|
||||||
|
final var logoutMenuItem = new MenuItem("Logout");
|
||||||
|
logoutMenuItem.addActionListener(evt -> { hide(); UserUtil.logout(); });
|
||||||
|
popup.add(exitMenuItem);
|
||||||
delvh marked this conversation as resolved
Outdated
kske
commented
This appears to be an error. This appears to be an error.
delvh
commented
Ctrl C, Ctrl V. Ctrl C, Ctrl V.
|
|||||||
|
|
||||||
|
// 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);
|
trayIcon.setPopupMenu(popup);
|
||||||
|
|
||||||
// Only display messages if the stage is not focused
|
// Only display messages if the stage is not focused
|
||||||
@ -89,9 +105,8 @@ public final class StatusTrayIcon implements EventListener {
|
|||||||
|
|
||||||
@Event
|
@Event
|
||||||
private void onMessage(Message message) {
|
private void onMessage(Message message) {
|
||||||
if (displayMessages) trayIcon.displayMessage(
|
if (displayMessages) trayIcon
|
||||||
delvh marked this conversation as resolved
Outdated
kske
commented
I think the point of the "busy" status is that the user does not want to be disturbed, so maybe put a check in place that prevents the notification in this status. Also, could we keep the previous formatting of this section? The new one looks very confusing to me. I think the point of the "busy" status is that the user does not want to be disturbed, so maybe put a check in place that prevents the notification in this status.
Also, could we keep the previous formatting of this section? The new one looks very confusing to me.
|
|||||||
message.hasAttachment() ? "New " + message.getAttachment().getType().toString().toLowerCase() + " message received" : "New message received",
|
.displayMessage(message.hasAttachment() ? "New " + message.getAttachment().getType().toString().toLowerCase() + " message received"
|
||||||
message.getText(),
|
: "New message received", message.getText(), MessageType.INFO);
|
||||||
MessageType.INFO);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,8 @@ import java.util.Random;
|
|||||||
import java.util.function.*;
|
import java.util.function.*;
|
||||||
import java.util.logging.Level;
|
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 javafx.scene.control.skin.VirtualFlow;
|
||||||
|
|
||||||
import envoy.client.data.Context;
|
import envoy.client.data.Context;
|
||||||
@ -14,6 +15,7 @@ import envoy.client.ui.SceneContext.SceneInfo;
|
|||||||
import envoy.client.ui.controller.ChatScene;
|
import envoy.client.ui.controller.ChatScene;
|
||||||
import envoy.client.util.*;
|
import envoy.client.util.*;
|
||||||
import envoy.data.Message;
|
import envoy.data.Message;
|
||||||
|
import envoy.data.User.UserStatus;
|
||||||
import envoy.util.EnvoyLog;
|
import envoy.util.EnvoyLog;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -63,6 +65,17 @@ public final class ChatSceneCommands {
|
|||||||
.setDescription("Opens the settings screen")
|
.setDescription("Opens the settings screen")
|
||||||
.buildNoArg("settings");
|
.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
kske
commented
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.
delvh
commented
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. 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.
kske
commented
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");
|
||||||
kske
commented
Any reason why the default is "offline"? This should be documented. Any reason why the default is "offline"? This should be documented.
delvh
commented
to 1. Well, normally when executing this command I'd argue that you want to change it, (from "ONLINE" in the standard case) and "OFFLINE" seemed like the most reasonable value. If you want another value, I can change that, i.e. BUSY? to 1. Well, normally when executing this command I'd argue that you want to change it, (from "ONLINE" in the standard case) and "OFFLINE" seemed like the most reasonable value. If you want another value, I can change that, i.e. BUSY?
to 2. Where should I document it? Our wiki is a laughable joke and the only location where it could be partially useful for the consumer would be inside the description of this system command. Should I add it there?
kske
commented
1. In my opinion, there is no sensible default. Rather, the parameter should be mandatory.
2. If you would put the documentation inside the wiki, it would become less of a joke. Maybe it's also possible to display the default values to the user in some way?
delvh
commented
1. Okay, I can remove the default value. Even though that completely defies the purpose of default values.
2. I've already thought about doing that (inspired by Skype)
but that'll require some major changes to the underlying system command system (=>another branch)
kske
commented
To my understanding, default values are only used if there is a default that actually makes sense. In case of status changes, I don't see, why a specific status would be the most frequently used one. To my understanding, default values are only used if there is a default that actually makes sense. In case of status changes, I don't see, why a specific status would be the most frequently used one.
delvh
commented
Have a look at the declaration again. Already fixed. Have a look at the declaration again. Already fixed.
|
|||||||
|
|
||||||
// Selection of a new message initialization
|
// Selection of a new message initialization
|
||||||
messageDependantAction("s",
|
messageDependantAction("s",
|
||||||
m -> { messageList.getSelectionModel().clearSelection(); messageList.getSelectionModel().select(m); },
|
m -> { messageList.getSelectionModel().clearSelection(); messageList.getSelectionModel().select(m); },
|
||||||
|
@ -55,18 +55,16 @@ public final class GeneralSettingsPane extends SettingsPane {
|
|||||||
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);
|
getChildren().add(combobox);
|
||||||
|
|
||||||
if (context.getClient().isOnline()) {
|
final var statusComboBox = new ComboBox<UserStatus>();
|
||||||
final var statusComboBox = new ComboBox<UserStatus>();
|
statusComboBox.getItems().setAll(UserStatus.values());
|
||||||
statusComboBox.getItems().setAll(UserStatus.values());
|
statusComboBox.setValue(context.getLocalDB().getUser().getStatus());
|
||||||
statusComboBox.setValue(context.getLocalDB().getUser().getStatus());
|
statusComboBox.setTooltip(new Tooltip("Change your current status"));
|
||||||
statusComboBox.setTooltip(new Tooltip("Change your current status"));
|
statusComboBox.setOnAction(e -> {
|
||||||
statusComboBox.setOnAction(e -> {
|
final var status = statusComboBox.getValue();
|
||||||
final var status = statusComboBox.getValue();
|
if (status == null) return;
|
||||||
kske
commented
How is this possible? How is this possible?
delvh
commented
Artifact from when I called it using the selectionModel before stumbling upon this method. Artifact from when I called it using the selectionModel before stumbling upon this method.
Yes, agreed, their Javadoc doesn't mention anything about it,so I'll remove it.
|
|||||||
if (status == null) return;
|
else UserUtil.changeStatus(status);
|
||||||
else UserUtil.changeStatus(status);
|
});
|
||||||
});
|
getChildren().add(statusComboBox);
|
||||||
getChildren().add(statusComboBox);
|
|
||||||
}
|
|
||||||
|
|
||||||
final var logoutButton = new Button("Logout");
|
final var logoutButton = new Button("Logout");
|
||||||
logoutButton.setOnAction(e -> UserUtil.logout());
|
logoutButton.setOnAction(e -> UserUtil.logout());
|
||||||
|
@ -53,6 +53,7 @@ public final class UserUtil {
|
|||||||
*/
|
*/
|
||||||
public static void changeStatus(UserStatus newStatus) {
|
public static void changeStatus(UserStatus newStatus) {
|
||||||
EventBus.getInstance().dispatch(new OwnStatusChange(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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
SceneContext
, that could be completely generic as to what scenes it displays, except for being littered with code specific toLoginScene
which nobody would expect here?(yes, these are both rhetorical questions)
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).
No, I think you are right. The cleanup should be performed inside a separate branch.
See #91