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,6 +297,7 @@ public final class ChatScene implements EventListener, Restorable {
chatList.setCellFactory(new ListCellFactory<>(ChatControl::new)); chatList.setCellFactory(new ListCellFactory<>(ChatControl::new));
messageList.setCellFactory(MessageListCell::new); messageList.setCellFactory(MessageListCell::new);
// TODO: cache image // TODO: cache image
if (currentChat != null)
if (currentChat.getRecipient() instanceof User) recipientProfilePic.setImage(IconUtil.loadIconThemeSensitive("user_icon", 43)); if (currentChat.getRecipient() instanceof User) recipientProfilePic.setImage(IconUtil.loadIconThemeSensitive("user_icon", 43));
else recipientProfilePic.setImage(IconUtil.loadIconThemeSensitive("group_icon", 43)); else recipientProfilePic.setImage(IconUtil.loadIconThemeSensitive("group_icon", 43));
} }

View File

@ -5,8 +5,6 @@ import javafx.scene.control.*;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
import javafx.stage.DirectoryChooser; import javafx.stage.DirectoryChooser;
import envoy.client.data.Context;
/** /**
* Displays options for downloading {@link envoy.data.Attachment}s. * Displays options for downloading {@link envoy.data.Attachment}s.
* *
@ -47,7 +45,7 @@ public final class DownloadSettingsPane extends SettingsPane {
final var directoryChooser = new DirectoryChooser(); final var directoryChooser = new DirectoryChooser();
directoryChooser.setTitle("Select the directory where attachments should be saved to"); directoryChooser.setTitle("Select the directory where attachments should be saved to");
directoryChooser.setInitialDirectory(settings.getDownloadLocation()); directoryChooser.setInitialDirectory(settings.getDownloadLocation());
final var selectedDirectory = directoryChooser.showDialog(Context.getInstance().getSceneContext().getStage()); final var selectedDirectory = directoryChooser.showDialog(context.getSceneContext().getStage());
if (selectedDirectory != null) { if (selectedDirectory != null) {
currentPath.setText(selectedDirectory.getAbsolutePath()); currentPath.setText(selectedDirectory.getAbsolutePath());

View File

@ -7,6 +7,7 @@ import envoy.client.event.ThemeChangeEvent;
import envoy.client.helper.ShutdownHelper; import envoy.client.helper.ShutdownHelper;
import envoy.client.ui.StatusTrayIcon; import envoy.client.ui.StatusTrayIcon;
import envoy.data.User.UserStatus; import envoy.data.User.UserStatus;
import envoy.event.UserStatusChange;
import dev.kske.eventbus.EventBus; 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()); }); 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(UserStatus.ONLINE); statusComboBox.setValue(context.getLocalDB().getUser().getStatus());
statusComboBox.setTooltip(new Tooltip("Change your current status")); statusComboBox.setTooltip(new Tooltip("Change your current status"));
// TODO add action when value is changed statusComboBox.setOnAction(e -> {
statusComboBox.setOnAction(e -> {}); 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); getChildren().add(statusComboBox);
}
final var logoutButton = new Button("Logout"); final var logoutButton = new Button("Logout");
logoutButton.setOnAction(e -> ShutdownHelper.logout()); logoutButton.setOnAction(e -> ShutdownHelper.logout());

View File

@ -5,7 +5,6 @@ import javafx.scene.control.*;
import javafx.scene.layout.*; import javafx.scene.layout.*;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import envoy.client.data.Context;
import envoy.client.net.Client; import envoy.client.net.Client;
/** /**
@ -20,7 +19,7 @@ import envoy.client.net.Client;
*/ */
public abstract class OnlineOnlySettingsPane extends SettingsPane { 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."); 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 javafx.scene.layout.VBox;
import envoy.client.data.Settings; import envoy.client.data.*;
/** /**
* @author Kai S. K. Engelbart * @author Kai S. K. Engelbart
@ -13,6 +13,7 @@ public abstract class SettingsPane extends VBox {
protected String title; 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; } 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.scene.layout.HBox;
import javafx.stage.FileChooser; import javafx.stage.FileChooser;
import envoy.client.data.Context;
import envoy.client.ui.control.ProfilePicImageView; import envoy.client.ui.control.ProfilePicImageView;
import envoy.client.util.IconUtil; import envoy.client.util.IconUtil;
import envoy.event.*; import envoy.event.*;
@ -66,7 +65,7 @@ public final class UserSettingsPane extends OnlineOnlySettingsPane {
pictureChooser.setInitialDirectory(new File(System.getProperty("user.home"))); pictureChooser.setInitialDirectory(new File(System.getProperty("user.home")));
pictureChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Pictures", "*.png", "*.jpg", "*.bmp", "*.gif")); 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) { if (file != null) {

View File

@ -6,7 +6,7 @@
-fx-background-radius: 15.0px; -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; -fx-background-color: #454c4f;
} }

View File

@ -18,7 +18,7 @@
-fx-background-color: lightgray; -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; -fx-background-color: #222222;
} }

View File

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

View File

@ -46,8 +46,7 @@ public final class LoginCredentialProcessor implements ObjectProcessor<LoginCred
// Acquire a user object (or reject the handshake if that's impossible) // Acquire a user object (or reject the handshake if that's impossible)
User user = null; User user = null;
if (!credentials.isRegistration()) { if (!credentials.isRegistration()) try {
try {
user = persistenceManager.getUserByName(credentials.getIdentifier()); user = persistenceManager.getUserByName(credentials.getIdentifier());
// Check if the user is already online // Check if the user is already online
@ -67,7 +66,7 @@ public final class LoginCredentialProcessor implements ObjectProcessor<LoginCred
writeProxy.write(socketID, new HandshakeRejection(INVALID_TOKEN)); writeProxy.write(socketID, new HandshakeRejection(INVALID_TOKEN));
return; return;
} }
} else { } else
// Check the password hash // Check the password hash
if (!PasswordUtil.validate(credentials.getPassword(), user.getPasswordHash())) { if (!PasswordUtil.validate(credentials.getPassword(), user.getPasswordHash())) {
@ -75,13 +74,12 @@ public final class LoginCredentialProcessor implements ObjectProcessor<LoginCred
writeProxy.write(socketID, new HandshakeRejection(WRONG_PASSWORD_OR_USER)); writeProxy.write(socketID, new HandshakeRejection(WRONG_PASSWORD_OR_USER));
return; return;
} }
} } catch (final NoResultException e) {
} catch (NoResultException e) {
logger.info("The requested user does not exist."); logger.info("The requested user does not exist.");
writeProxy.write(socketID, new HandshakeRejection(WRONG_PASSWORD_OR_USER)); writeProxy.write(socketID, new HandshakeRejection(WRONG_PASSWORD_OR_USER));
return; return;
} }
} else { else {
// Validate user name // Validate user name
if (!Bounds.isValidContactName(credentials.getIdentifier())) { if (!Bounds.isValidContactName(credentials.getIdentifier())) {
@ -98,7 +96,7 @@ public final class LoginCredentialProcessor implements ObjectProcessor<LoginCred
logger.info("The requested user already exists."); logger.info("The requested user already exists.");
writeProxy.write(socketID, new HandshakeRejection(USERNAME_TAKEN)); writeProxy.write(socketID, new HandshakeRejection(USERNAME_TAKEN));
return; return;
} catch (NoResultException e) { } catch (final NoResultException e) {
// Creation of a new user // Creation of a new user
user = new User(); user = new User();
user.setName(credentials.getIdentifier()); user.setName(credentials.getIdentifier());
@ -115,18 +113,17 @@ public final class LoginCredentialProcessor implements ObjectProcessor<LoginCred
connectionManager.registerUser(user.getID(), socketID); connectionManager.registerUser(user.getID(), socketID);
// Change status and notify contacts about it // Change status and notify contacts about it
user.setStatus(ONLINE); UserStatusChangeProcessor.updateUserStatus(user, ONLINE);
UserStatusChangeProcessor.updateUserStatus(user);
// Process token request // Process token request
if (credentials.requestToken()) { if (credentials.requestToken()) {
String token; 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 // Reuse existing token and delay expiration date
token = user.getAuthToken(); token = user.getAuthToken();
} else { else {
// Generate new token // Generate new token
token = AuthTokenGenerator.nextToken(); token = AuthTokenGenerator.nextToken();
@ -141,13 +138,13 @@ public final class LoginCredentialProcessor implements ObjectProcessor<LoginCred
pendingMessages.removeIf(GroupMessage.class::isInstance); pendingMessages.removeIf(GroupMessage.class::isInstance);
logger.fine("Sending " + pendingMessages.size() + " pending messages to " + user + "..."); logger.fine("Sending " + pendingMessages.size() + " pending messages to " + user + "...");
for (var msg : pendingMessages) { for (final var msg : pendingMessages) {
final var msgCommon = msg.toCommon(); 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 // Sync without side effects
writeProxy.write(socketID, msgCommon); writeProxy.write(socketID, msgCommon);
} else if (msg.getStatus() == SENT) { else if (msg.getStatus() == SENT) {
// Send the message // Send the message
writeProxy.write(socketID, msgCommon); writeProxy.write(socketID, msgCommon);
@ -162,10 +159,10 @@ public final class LoginCredentialProcessor implements ObjectProcessor<LoginCred
} else writeProxy.write(socketID, new MessageStatusChange(msgCommon)); } 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 + "..."); logger.fine("Sending " + pendingGroupMessages.size() + " pending group messages to " + user + "...");
for (var gmsg : pendingGroupMessages) { for (final var gmsg : pendingGroupMessages) {
final var gmsgCommon = gmsg.toCommon(); final var gmsgCommon = gmsg.toCommon();
// Deliver the message to the user if he hasn't received it yet // 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); PersistenceManager.getInstance().updateMessage(gmsg);
} else { } else
// Just send the message without updating if it was received in the past // Just send the message without updating if it was received in the past
writeProxy.write(socketID, gmsgCommon); writeProxy.write(socketID, gmsgCommon);
}
} else { } else {
// Sending group message status changes // Sending group message status changes
if (gmsg.getStatus() == SENT && gmsg.getLastStatusChangeDate().isAfter(gmsg.getCreationDate()) 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() gmsg.getMemberMessageStatus()
.forEach((memberID, memberStatus) -> writeProxy.write(socketID, .forEach((memberID, memberStatus) -> writeProxy.write(socketID,
new GroupMessageStatusChange(gmsg.getID(), memberStatus, gmsg.getLastStatusChangeDate(), memberID))); new GroupMessageStatusChange(gmsg.getID(), memberStatus, gmsg.getLastStatusChangeDate(), memberID)));
}
// Deliver just a status change instead of the whole message // Deliver just a status change instead of the whole message
if (gmsg.getStatus() == RECEIVED && user.getLastSeen().isBefore(gmsg.getReceivedDate()) 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"); logger.warning("Received an unnecessary UserStatusChange");
return; return;
} }
updateUserStatus(input); updateUserStatus(persistenceManager.getUserByID(input.getID()), input.get());
} }
/** /**
* Sets the {@link UserStatus} for a given user. Both offline contacts and * Sets the {@link UserStatus} for a given user. Both offline contacts and
* currently online contacts are notified. * 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 * @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 // Handling for newly logged in clients
persistenceManager.updateContact(user); persistenceManager.updateContact(user);
@ -48,12 +50,6 @@ public final class UserStatusChangeProcessor implements ObjectProcessor<UserStat
writeProxy.writeToOnlineContacts(user.getContacts(), new UserStatusChange(user.getID(), user.getStatus())); 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 * This method is only called by the LoginCredentialProcessor because every
* user needs to login (open a socket) before changing his status. * user needs to login (open a socket) before changing his status.