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
11 changed files with 81 additions and 81 deletions
Showing only changes of commit 637ad9f61f - Show all commits

View File

@ -297,8 +297,9 @@ public final class ChatScene implements EventListener, Restorable {
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));
if (currentChat != null)
if (currentChat.getRecipient() instanceof User) recipientProfilePic.setImage(IconUtil.loadIconThemeSensitive("user_icon", 43));
else recipientProfilePic.setImage(IconUtil.loadIconThemeSensitive("group_icon", 43));
}
@Event(eventType = Logout.class, priority = 200)

View File

@ -5,8 +5,6 @@ import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import javafx.stage.DirectoryChooser;
import envoy.client.data.Context;
/**
* Displays options for downloading {@link envoy.data.Attachment}s.
*
@ -47,7 +45,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(Context.getInstance().getSceneContext().getStage());
final var selectedDirectory = directoryChooser.showDialog(context.getSceneContext().getStage());
if (selectedDirectory != null) {
currentPath.setText(selectedDirectory.getAbsolutePath());

View File

@ -7,6 +7,7 @@ import envoy.client.event.ThemeChangeEvent;
import envoy.client.helper.ShutdownHelper;
import envoy.client.ui.StatusTrayIcon;
import envoy.data.User.UserStatus;
import envoy.event.UserStatusChange;
import dev.kske.eventbus.EventBus;
@ -55,13 +56,23 @@ public final class GeneralSettingsPane extends SettingsPane {
combobox.setOnAction(e -> { settings.setCurrentTheme(combobox.getValue()); EventBus.getInstance().dispatch(new ThemeChangeEvent()); });
getChildren().add(combobox);
final var statusComboBox = new ComboBox<UserStatus>();
statusComboBox.getItems().setAll(UserStatus.values());
statusComboBox.setValue(UserStatus.ONLINE);
statusComboBox.setTooltip(new Tooltip("Change your current status"));
// TODO add action when value is changed
statusComboBox.setOnAction(e -> {});
getChildren().add(statusComboBox);
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 -> {
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.
final var status = statusComboBox.getValue();
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));
}
});
getChildren().add(statusComboBox);
}
final var logoutButton = new Button("Logout");
logoutButton.setOnAction(e -> ShutdownHelper.logout());

View File

@ -5,7 +5,6 @@ import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import envoy.client.data.Context;
import envoy.client.net.Client;
/**
@ -20,7 +19,7 @@ import envoy.client.net.Client;
*/
public abstract class OnlineOnlySettingsPane extends SettingsPane {
protected final Client client = Context.getInstance().getClient();
protected final Client client = context.getClient();
private final Tooltip beOnlineReminder = new Tooltip("You need to be online to modify your account.");

View File

@ -2,7 +2,7 @@ package envoy.client.ui.settings;
import javafx.scene.layout.VBox;
import envoy.client.data.Settings;
import envoy.client.data.*;
/**
* @author Kai S. K. Engelbart
@ -12,7 +12,8 @@ public abstract class SettingsPane extends VBox {
protected String title;
protected static final Settings settings = Settings.getInstance();
protected static final Settings settings = Settings.getInstance();
protected static final Context context = Context.getInstance();
protected SettingsPane(String title) { this.title = title; }

View File

@ -14,7 +14,6 @@ import javafx.scene.input.InputEvent;
import javafx.scene.layout.HBox;
import javafx.stage.FileChooser;
import envoy.client.data.Context;
import envoy.client.ui.control.ProfilePicImageView;
import envoy.client.util.IconUtil;
import envoy.event.*;
@ -66,7 +65,7 @@ public final class UserSettingsPane extends OnlineOnlySettingsPane {
pictureChooser.setInitialDirectory(new File(System.getProperty("user.home")));
pictureChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Pictures", "*.png", "*.jpg", "*.bmp", "*.gif"));
final var file = pictureChooser.showOpenDialog(Context.getInstance().getSceneContext().getStage());
final var file = pictureChooser.showOpenDialog(context.getSceneContext().getStage());
if (file != null) {

View File

@ -6,7 +6,7 @@
-fx-background-radius: 15.0px;
}
.list-cell:selected, .menu-item:hover {
.list-cell:selected, .menu-item:hover, .combo-box-popup .list-view .list-cell:selected {
-fx-background-color: #454c4f;
}

View File

@ -18,7 +18,7 @@
-fx-background-color: lightgray;
}
#message-list, .text-field, .password-field, .tooltip, .pane, .pane .content, .vbox, .titled-pane > .title, .titled-pane > *.content, .context-menu, .menu-item, #quick-select-list {
#message-list, .text-field, .password-field, .tooltip, .pane, .pane .content, .vbox, .titled-pane > .title, .titled-pane > *.content, .context-menu, .menu-item, .combo-box-popup .list-view .list-cell, #quick-select-list {
-fx-background-color: #222222;
}

View File

@ -22,14 +22,14 @@ public final class ConnectionManager implements ISocketIdListener {
*
* @since Envoy Server Standalone v0.1-alpha
*/
private Set<Long> pendingSockets = new HashSet<>();
private final Set<Long> pendingSockets = new HashSet<>();
/**
* Contains all socket IDs that have acquired a user ID as keys to these IDs.
*
* @since Envoy Server Standalone v0.1-alpha
*/
private Map<Long, Long> sockets = new HashMap<>();
private final Map<Long, Long> sockets = new HashMap<>();
private static ConnectionManager connectionManager = new ConnectionManager();
@ -44,11 +44,11 @@ public final class ConnectionManager implements ISocketIdListener {
@Override
public void socketCancelled(long socketID) {
if (!pendingSockets.remove(socketID)) {
// Notify contacts of this users offline-going
envoy.server.data.User user = PersistenceManager.getInstance().getUserByID(getUserIDBySocketID(socketID));
user.setStatus(UserStatus.OFFLINE);
final envoy.server.data.User user = PersistenceManager.getInstance().getUserByID(getUserIDBySocketID(socketID));
user.setLastSeen(Instant.now());
UserStatusChangeProcessor.updateUserStatus(user);
UserStatusChangeProcessor.updateUserStatus(user, UserStatus.OFFLINE);
// Remove the socket
sockets.entrySet().removeIf(e -> e.getValue() == socketID);

View File

@ -46,42 +46,40 @@ public final class LoginCredentialProcessor implements ObjectProcessor<LoginCred
// Acquire a user object (or reject the handshake if that's impossible)
User user = null;
if (!credentials.isRegistration()) {
try {
user = persistenceManager.getUserByName(credentials.getIdentifier());
if (!credentials.isRegistration()) try {
user = persistenceManager.getUserByName(credentials.getIdentifier());
// Check if the user is already online
if (connectionManager.isOnline(user.getID())) {
logger.warning(user + " is already online!");
writeProxy.write(socketID, new HandshakeRejection(INTERNAL_ERROR));
return;
}
// Authenticate with password or token
if (credentials.usesToken()) {
// Check the token
if (user.getAuthToken() == null || user.getAuthTokenExpiration().isBefore(Instant.now())
|| !user.getAuthToken().equals(credentials.getPassword())) {
logger.info(user + " tried to use an invalid token.");
writeProxy.write(socketID, new HandshakeRejection(INVALID_TOKEN));
return;
}
} else {
// Check the password hash
if (!PasswordUtil.validate(credentials.getPassword(), user.getPasswordHash())) {
logger.info(user + " has entered the wrong password.");
writeProxy.write(socketID, new HandshakeRejection(WRONG_PASSWORD_OR_USER));
return;
}
}
} catch (NoResultException e) {
logger.info("The requested user does not exist.");
writeProxy.write(socketID, new HandshakeRejection(WRONG_PASSWORD_OR_USER));
// Check if the user is already online
if (connectionManager.isOnline(user.getID())) {
logger.warning(user + " is already online!");
writeProxy.write(socketID, new HandshakeRejection(INTERNAL_ERROR));
return;
}
} else {
// Authenticate with password or token
if (credentials.usesToken()) {
// Check the token
if (user.getAuthToken() == null || user.getAuthTokenExpiration().isBefore(Instant.now())
|| !user.getAuthToken().equals(credentials.getPassword())) {
logger.info(user + " tried to use an invalid token.");
writeProxy.write(socketID, new HandshakeRejection(INVALID_TOKEN));
return;
}
} else
// Check the password hash
if (!PasswordUtil.validate(credentials.getPassword(), user.getPasswordHash())) {
logger.info(user + " has entered the wrong password.");
writeProxy.write(socketID, new HandshakeRejection(WRONG_PASSWORD_OR_USER));
return;
}
} catch (final NoResultException e) {
logger.info("The requested user does not exist.");
writeProxy.write(socketID, new HandshakeRejection(WRONG_PASSWORD_OR_USER));
return;
}
else {
// Validate user name
if (!Bounds.isValidContactName(credentials.getIdentifier())) {
@ -98,7 +96,7 @@ public final class LoginCredentialProcessor implements ObjectProcessor<LoginCred
logger.info("The requested user already exists.");
writeProxy.write(socketID, new HandshakeRejection(USERNAME_TAKEN));
return;
} catch (NoResultException e) {
} catch (final NoResultException e) {
// Creation of a new user
user = new User();
user.setName(credentials.getIdentifier());
@ -115,18 +113,17 @@ public final class LoginCredentialProcessor implements ObjectProcessor<LoginCred
connectionManager.registerUser(user.getID(), socketID);
// Change status and notify contacts about it
user.setStatus(ONLINE);
UserStatusChangeProcessor.updateUserStatus(user);
UserStatusChangeProcessor.updateUserStatus(user, ONLINE);
// Process token request
if (credentials.requestToken()) {
String token;
if (user.getAuthToken() != null && user.getAuthTokenExpiration().isAfter(Instant.now())) {
if (user.getAuthToken() != null && user.getAuthTokenExpiration().isAfter(Instant.now()))
mpk marked this conversation as resolved
Review

Are you sure all of the code in the body of this if statement gets executed if you remove the curly brackets?

Are you sure all of the code in the body of this if statement gets executed if you remove the curly brackets?
Review

Was my formatter, so I'd guess so. Otherwise blame Eclipse.
Same for below.

Was my formatter, so I'd guess so. Otherwise blame Eclipse. Same for below.
Review

And yes, it's only one statement, so why shouldn't it?

And yes, it's only one statement, so why shouldn't it?
Review

Because of the comment but I guess it does not count

Because of the comment but I guess it does not count
// Reuse existing token and delay expiration date
token = user.getAuthToken();
} else {
else {
// Generate new token
token = AuthTokenGenerator.nextToken();
@ -141,13 +138,13 @@ public final class LoginCredentialProcessor implements ObjectProcessor<LoginCred
pendingMessages.removeIf(GroupMessage.class::isInstance);
logger.fine("Sending " + pendingMessages.size() + " pending messages to " + user + "...");
for (var msg : pendingMessages) {
for (final var msg : pendingMessages) {
final var msgCommon = msg.toCommon();
if (msg.getCreationDate().isAfter(credentials.getLastSync())) {
if (msg.getCreationDate().isAfter(credentials.getLastSync()))
mpk marked this conversation as resolved
Review

same as above

same as above
Review

Still only one statement.

Still only one statement.
// Sync without side effects
writeProxy.write(socketID, msgCommon);
} else if (msg.getStatus() == SENT) {
else if (msg.getStatus() == SENT) {
// Send the message
writeProxy.write(socketID, msgCommon);
@ -162,10 +159,10 @@ public final class LoginCredentialProcessor implements ObjectProcessor<LoginCred
} else writeProxy.write(socketID, new MessageStatusChange(msgCommon));
}
List<GroupMessage> pendingGroupMessages = PersistenceManager.getInstance().getPendingGroupMessages(user, credentials.getLastSync());
final List<GroupMessage> pendingGroupMessages = PersistenceManager.getInstance().getPendingGroupMessages(user, credentials.getLastSync());
logger.fine("Sending " + pendingGroupMessages.size() + " pending group messages to " + user + "...");
for (var gmsg : pendingGroupMessages) {
for (final var gmsg : pendingGroupMessages) {
final var gmsgCommon = gmsg.toCommon();
// Deliver the message to the user if he hasn't received it yet
@ -189,20 +186,18 @@ public final class LoginCredentialProcessor implements ObjectProcessor<LoginCred
}
PersistenceManager.getInstance().updateMessage(gmsg);
} else {
} else
// Just send the message without updating if it was received in the past
writeProxy.write(socketID, gmsgCommon);
}
} else {
// Sending group message status changes
if (gmsg.getStatus() == SENT && gmsg.getLastStatusChangeDate().isAfter(gmsg.getCreationDate())
|| gmsg.getStatus() == RECEIVED && gmsg.getLastStatusChangeDate().isAfter(gmsg.getReceivedDate())) {
|| gmsg.getStatus() == RECEIVED && gmsg.getLastStatusChangeDate().isAfter(gmsg.getReceivedDate()))
mpk marked this conversation as resolved
Review

same as above

same as above
Review

Still only one statement

Still only one statement
gmsg.getMemberMessageStatus()
.forEach((memberID, memberStatus) -> writeProxy.write(socketID,
new GroupMessageStatusChange(gmsg.getID(), memberStatus, gmsg.getLastStatusChangeDate(), memberID)));
}
// Deliver just a status change instead of the whole message
if (gmsg.getStatus() == RECEIVED && user.getLastSeen().isBefore(gmsg.getReceivedDate())

View File

@ -28,18 +28,20 @@ public final class UserStatusChangeProcessor implements ObjectProcessor<UserStat
logger.warning("Received an unnecessary UserStatusChange");
return;
}
updateUserStatus(input);
updateUserStatus(persistenceManager.getUserByID(input.getID()), input.get());
}
/**
* Sets the {@link UserStatus} for a given user. Both offline contacts and
* currently online contacts are notified.
*
* @param user the {@link UserStatusChange} that signals the change
* @param user the user whose status has changed
* @param newStatus the new status of that user
* @since Envoy Server Standalone v0.1-alpha
*/
public static void updateUserStatus(User user) {
public static void updateUserStatus(User user, UserStatus newStatus) {
user.setStatus(newStatus);
// Handling for newly logged in clients
persistenceManager.updateContact(user);
@ -48,12 +50,6 @@ public final class UserStatusChangeProcessor implements ObjectProcessor<UserStat
writeProxy.writeToOnlineContacts(user.getContacts(), new UserStatusChange(user.getID(), user.getStatus()));
}
/**
* @param evt the {@link UserStatusChange}
* @since Envoy Server Standalone v0.1-alpha
*/
public static void updateUserStatus(UserStatusChange evt) { updateUserStatus(persistenceManager.getUserByID(evt.getID())); }
/**
* This method is only called by the LoginCredentialProcessor because every
* user needs to login (open a socket) before changing his status.