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
9 changed files with 127 additions and 73 deletions
Showing only changes of commit e8202e0c94 - Show all commits

View File

@ -293,6 +293,9 @@ public final class LocalDB implements EventListener {
});
}
@Event(priority = 500)
private void onOwnStatusChange(OwnStatusChange statusChange) { user.setStatus(statusChange.get()); }
/**
* @return a {@code Map<String, User>} of all users stored locally with their
* user names as keys

View File

@ -0,0 +1,23 @@
package envoy.client.event;
import envoy.data.User;
import envoy.data.User.UserStatus;
import envoy.event.Event;
/**
* Conveys the action that the currently logged in {@link User} has changed his
delvh marked this conversation as resolved
Review

This construction is complicated even for my standards.

What about "Signifies a manual status change of the client user".

This construction is complicated even for my standards. What about "Signifies a manual status change of the client user".
* status (manually).
*
* @author Leon Hofmeister
* @since Envoy Client v0.3-beta
*/
public class OwnStatusChange extends Event<UserStatus> {
private static final long serialVersionUID = 1L;
/**
* @param value the new user status of the currently logged in user
* @since Envoy Client v0.3-beta
*/
public OwnStatusChange(UserStatus value) { super(value); }
}

View File

@ -1,15 +1,8 @@
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.client.event.EnvoyCloseEvent;
import envoy.client.ui.StatusTrayIcon;
import envoy.util.EnvoyLog;
import dev.kske.eventbus.EventBus;
@ -36,23 +29,4 @@ public final class ShutdownHelper {
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

@ -13,6 +13,7 @@ import javafx.stage.Stage;
import envoy.client.data.Settings;
import envoy.client.event.*;
import envoy.client.helper.ShutdownHelper;
import envoy.client.util.UserUtil;
import envoy.util.EnvoyLog;
import dev.kske.eventbus.*;
@ -128,7 +129,7 @@ public final class SceneContext implements EventListener {
// 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
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
accelerators.put(new KeyCodeCombination(KeyCode.L, KeyCombination.CONTROL_DOWN, KeyCombination.SHIFT_DOWN), ShutdownHelper::logout);
accelerators.put(new KeyCodeCombination(KeyCode.L, KeyCombination.CONTROL_DOWN, KeyCombination.SHIFT_DOWN), UserUtil::logout);
// Add the option to open the settings scene with "Control"+"S", if being in
// chat scene

View File

@ -12,7 +12,7 @@ import envoy.client.data.commands.*;
import envoy.client.helper.ShutdownHelper;
import envoy.client.ui.SceneContext.SceneInfo;
import envoy.client.ui.controller.ChatScene;
import envoy.client.util.MessageUtil;
import envoy.client.util.*;
import envoy.data.Message;
import envoy.util.EnvoyLog;
@ -52,7 +52,7 @@ public final class ChatSceneCommands {
.build("dabr");
// Logout initialization
builder.setAction(text -> ShutdownHelper.logout()).setDescription("Logs you out.").buildNoArg("logout");
builder.setAction(text -> UserUtil.logout()).setDescription("Logs you out.").buildNoArg("logout");
// Exit initialization
builder.setAction(text -> ShutdownHelper.exit()).setNumberOfArguments(0).setDescription("Exits the program.").build("exit", false);

View File

@ -13,6 +13,7 @@ import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.fxml.*;
import javafx.geometry.Pos;
import javafx.scene.control.*;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.image.*;
@ -29,7 +30,7 @@ import envoy.client.event.*;
import envoy.client.net.*;
import envoy.client.ui.*;
import envoy.client.ui.chatscene.*;
import envoy.client.ui.control.ChatControl;
import envoy.client.ui.control.*;
import envoy.client.ui.listcell.*;
import envoy.client.util.*;
import envoy.data.*;
@ -78,9 +79,6 @@ public final class ChatScene implements EventListener, Restorable {
@FXML
private Button newContactButton;
@FXML
private Label contactLabel;
@FXML
private Label remainingChars;
@ -126,6 +124,12 @@ public final class ChatScene implements EventListener, Restorable {
@FXML
private HBox contactSpecificOnlineOperations;
@FXML
private HBox ownContactControl;
@FXML
private Region spaceBetweenUserAndSettingsButton;
private Chat currentChat;
private FilteredList<Chat> chats;
private boolean recording;
@ -185,10 +189,16 @@ public final class ChatScene implements EventListener, Restorable {
clientProfilePic.setClip(clip);
chatList.setItems(chats = new FilteredList<>(localDB.getChats()));
contactLabel.setText(localDB.getUser().getName());
// Set the design of the box in the upper-left corner
settingsButton.setAlignment(Pos.BOTTOM_RIGHT);
HBox.setHgrow(spaceBetweenUserAndSettingsButton, Priority.ALWAYS);
delvh marked this conversation as resolved
Review

@DieGurke The region you requested is already here, but for some strange reason it doesn't work as expected.
Any idea why?
Feel free to take on that challenge, because I've tried it for about twenty minutes and could not get it to work.

@DieGurke The region you requested is already here, but for some strange reason it doesn't work as expected. Any idea why? Feel free to take on that challenge, because I've tried it for about twenty minutes and could not get it to work.
Review

I could't fix it neither. Preferably you open an issue for that.

I could't fix it neither. Preferably you open an issue for that.
generateOwnStatusControl();
System.out.println(ownContactControl.getChildren());
delvh marked this conversation as resolved Outdated
Outdated
Review

This seems to be a leftover from development.

This seems to be a leftover from development.
Platform.runLater(() -> {
final var online = client.isOnline();
// no check will be performed in case it has already been disabled - a negative
// GroupCreationResult might have been returned
if (!newGroupButton.isDisabled()) newGroupButton.setDisable(!online);
@ -260,6 +270,9 @@ public final class ChatScene implements EventListener, Restorable {
});
}
@Event(eventType = OwnStatusChange.class, priority = 50)
private void onOwnStatusChange() { generateOwnStatusControl(); }
@Event
private void onContactOperation(ContactOperation operation) {
final var contact = operation.get();
@ -706,6 +719,13 @@ public final class ChatScene implements EventListener, Restorable {
attachmentView.setVisible(visible);
}
private void generateOwnStatusControl() {
delvh marked this conversation as resolved
Review

You could annotate this as an event handler directly.

You could annotate this as an event handler directly.
final var ownUserControl = new ContactControl(localDB.getUser());
Outdated
Review

I do not see the point of doing this as you can just check if it is null if I understand that correctly. If not please correct and explane the exact purpose to me.

I do not see the point of doing this as you can just check if it is null if I understand that correctly. If not please correct and explane the exact purpose to me.
Outdated
Review

As you can see a few lines above, this method is annotated with an Event: It gets called every time the user changes his own status.
Hence I always have to replace it then.
And for the first part: I cannot do that because initially this component is no part of the HBox (you remember, the thing with importing the by now outdated jar into the SceneBuilder...). To avoid that, it gets dynamically added.

But thinking about it, it may be possible, however I do not know if it'll work as bug-free as this implementation: As soon as you initialize this variable somewhere else, you might encounter a bug that duplicates it.

As you can see a few lines above, this method is annotated with an Event: It gets called every time the user changes his own status. Hence I always have to replace it then. And for the first part: I cannot do that because initially this component is no part of the HBox (you remember, the thing with importing the by now outdated jar into the SceneBuilder...). To avoid that, it gets dynamically added. But thinking about it, it may be possible, however I do not know if it'll work as bug-free as this implementation: As soon as you initialize this variable somewhere else, you might encounter a bug that duplicates it.
Outdated
Review

What if, instead of regenerating the whole control, we would just change the displayed information in the event handler?

What if, instead of regenerating the whole control, we would just change the displayed information in the event handler?
Outdated
Review

It could be possible if we make some changes to the underlying ContactControl.
The question however is: Should it be done?

It could be possible if we make some changes to the underlying `ContactControl`. The question however is: Should it be done?
Outdated
Review

Oh, and also: there is a difference between ownUserControl and ownContactControl.

Oh, and also: there is a difference between ownUserControl and ownContactControl.
Outdated
Review

Better?

Better?
Outdated
Review

Yes better

Yes better
ownUserControl.setAlignment(Pos.CENTER_LEFT);
if (ownContactControl.getChildren().get(0) instanceof ContactControl) ownContactControl.getChildren().set(0, ownUserControl);
else ownContactControl.getChildren().add(0, ownUserControl);
}
// Context menu actions
@FXML

View File

@ -4,10 +4,9 @@ import javafx.scene.control.*;
import envoy.client.data.SettingsItem;
import envoy.client.event.ThemeChangeEvent;
import envoy.client.helper.ShutdownHelper;
import envoy.client.ui.StatusTrayIcon;
import envoy.client.util.UserUtil;
import envoy.data.User.UserStatus;
import envoy.event.UserStatusChange;
import dev.kske.eventbus.EventBus;
@ -64,18 +63,13 @@ public final class GeneralSettingsPane extends SettingsPane {
statusComboBox.setOnAction(e -> {
final var status = statusComboBox.getValue();
Outdated
Review

How is this possible?

How is this possible?
Outdated
Review

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.

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 {
final var user = context.getLocalDB().getUser();
user.setStatus(status);
// TODO update status in ChatScene
context.getClient().send(new UserStatusChange(user.getID(), status));
}
else UserUtil.changeStatus(status);
});
getChildren().add(statusComboBox);
}
final var logoutButton = new Button("Logout");
logoutButton.setOnAction(e -> ShutdownHelper.logout());
logoutButton.setOnAction(e -> UserUtil.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);

View File

@ -0,0 +1,58 @@
package envoy.client.util;
import java.util.logging.Level;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import envoy.client.data.Context;
import envoy.client.event.*;
import envoy.client.helper.*;
import envoy.client.ui.SceneContext.SceneInfo;
import envoy.data.User.UserStatus;
import envoy.event.UserStatusChange;
import envoy.util.EnvoyLog;
import dev.kske.eventbus.EventBus;
/**
* Contains methods that change something about the currently logged in user.
*
* @author Leon Hofmeister
* @since Envoy Client v0.3-beta
*/
public final class UserUtil {
private UserUtil() {}
/**
* 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);
});
}
/**
* Notifies the application that the status of the currently logged in user has
* changed
*
* @param newStatus the new status
* @since Envoy Client v0.3-beta
*/
public static void changeStatus(UserStatus newStatus) {
EventBus.getInstance().dispatch(new OwnStatusChange(newStatus));
Context.getInstance().getClient().send(new UserStatusChange(Context.getInstance().getLocalDB().getUser().getID(), newStatus));
}
}

View File

@ -161,44 +161,25 @@
fitHeight="43.0" fitWidth="43.0" pickOnBounds="true"
preserveRatio="true">
<HBox.margin>
<Insets left="15.0" top="5.0" />
<Insets left="15.0" top="5.0" right="10.0" />
</HBox.margin>
</ImageView>
<Label id="transparent-background" fx:id="contactLabel"
prefHeight="27.0" prefWidth="134.0">
<padding>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
</padding>
<font>
<Font size="18.0" />
</font>
<HBox.margin>
<Insets left="10.0" top="5.0" />
</HBox.margin>
</Label>
<Region id="transparent-background" prefHeight="77.0"
prefWidth="115.0" />
<VBox id="transparent-background" alignment="CENTER_RIGHT"
prefHeight="200.0" prefWidth="100.0" spacing="5.0">
<HBox id="transparent-background" fx:id="ownContactControl">
<children>
<Button fx:id="settingsButton" mnemonicParsing="true"
<Region id="transparent-background" prefWidth="120"
fx:id="spaceBetweenUserAndSettingsButton" />
<Button fx:id="settingsButton" mnemonicParsing="false"
onAction="#settingsButtonClicked" prefHeight="30.0"
prefWidth="30.0" text="">
prefWidth="30.0" text="" alignment="CENTER">
<padding>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
</padding>
<VBox.margin>
<Insets />
</VBox.margin>
<HBox.margin>
<Insets bottom="35.0" left="5.0" top="35.0"/>
</HBox.margin>
</Button>
</children>
<HBox.margin>
<Insets right="10.0" />
</HBox.margin>
<opaqueInsets>
<Insets />
</opaqueInsets>
</VBox>
</HBox>
</children>
<GridPane.margin>
<Insets bottom="1.0" right="1.0" />