Move context menu from ChatScene globally to ChatControl specific

Additionally fixed a small bug in UserCreationProcessor and when
deleting a contact offline
This commit is contained in:
Leon Hofmeister 2020-10-14 23:41:24 +02:00
parent a91181b4dd
commit bdb964a0d1
Signed by: delvh
GPG Key ID: 3DECE05F6D9A647C
8 changed files with 76 additions and 89 deletions

View File

@ -1,12 +1,13 @@
package envoy.client.ui.control; package envoy.client.ui.control;
import javafx.geometry.*; import javafx.geometry.*;
import javafx.scene.control.Label; import javafx.scene.control.*;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.layout.*; import javafx.scene.layout.*;
import envoy.client.data.*; import envoy.client.data.*;
import envoy.client.util.IconUtil; import envoy.client.util.*;
import envoy.data.User;
/** /**
* Displays a chat using a contact control for the recipient and a label for the * Displays a chat using a contact control for the recipient and a label for the
@ -16,7 +17,7 @@ import envoy.client.util.IconUtil;
* @author Leon Hofmeister * @author Leon Hofmeister
* @since Envoy Client v0.1-beta * @since Envoy Client v0.1-beta
*/ */
public final class ChatControl extends HBox { public final class ChatControl extends Label {
private static final Image userIcon = IconUtil.loadIconThemeSensitive("user_icon", 32), private static final Image userIcon = IconUtil.loadIconThemeSensitive("user_icon", 32),
groupIcon = IconUtil.loadIconThemeSensitive("group_icon", 32); groupIcon = IconUtil.loadIconThemeSensitive("group_icon", 32);
@ -31,25 +32,35 @@ public final class ChatControl extends HBox {
setAlignment(Pos.CENTER_LEFT); setAlignment(Pos.CENTER_LEFT);
setPadding(new Insets(0, 0, 3, 0)); setPadding(new Insets(0, 0, 3, 0));
final var menu = new ContextMenu();
final var removeMI = new MenuItem();
removeMI
.setText(chat.isDisabled() ? "Delete " : chat.getRecipient() instanceof User ? "Block " : "Leave group " + chat.getRecipient().getName());
removeMI.setOnAction(chat.isDisabled() ? e -> UserUtil.deleteContact(chat.getRecipient()) : e -> UserUtil.blockContact(chat.getRecipient()));
menu.getItems().add(removeMI);
setContextMenu(menu);
final var display = new HBox();
// Profile picture // Profile picture
final var contactProfilePic = new ProfilePicImageView(chat instanceof GroupChat ? groupIcon : userIcon, 32); final var contactProfilePic = new ProfilePicImageView(chat instanceof GroupChat ? groupIcon : userIcon, 32);
getChildren().add(contactProfilePic); display.getChildren().add(contactProfilePic);
// Spacing // Spacing
final var leftSpacing = new Region(); final var leftSpacing = new Region();
leftSpacing.setPrefSize(8, 0); leftSpacing.setPrefSize(8, 0);
leftSpacing.setMinSize(8, 0); leftSpacing.setMinSize(8, 0);
leftSpacing.setMaxSize(8, 0); leftSpacing.setMaxSize(8, 0);
getChildren().add(leftSpacing); display.getChildren().add(leftSpacing);
// Contact control // Contact control
getChildren().add(new ContactControl(chat.getRecipient())); display.getChildren().add(new ContactControl(chat.getRecipient()));
// Unread messages // Unread messages
if (chat.getUnreadAmount() != 0) { if (chat.getUnreadAmount() != 0) {
final var spacing = new Region(); final var spacing = new Region();
setHgrow(spacing, Priority.ALWAYS); HBox.setHgrow(spacing, Priority.ALWAYS);
getChildren().add(spacing); display.getChildren().add(spacing);
final var unreadMessagesLabel = new Label(Integer.toString(chat.getUnreadAmount())); final var unreadMessagesLabel = new Label(Integer.toString(chat.getUnreadAmount()));
unreadMessagesLabel.setMinSize(15, 15); unreadMessagesLabel.setMinSize(15, 15);
final var vbox = new VBox(); final var vbox = new VBox();
@ -57,9 +68,11 @@ public final class ChatControl extends HBox {
unreadMessagesLabel.setAlignment(Pos.CENTER); unreadMessagesLabel.setAlignment(Pos.CENTER);
unreadMessagesLabel.getStyleClass().add("unread-messages-amount"); unreadMessagesLabel.getStyleClass().add("unread-messages-amount");
vbox.getChildren().add(unreadMessagesLabel); vbox.getChildren().add(unreadMessagesLabel);
getChildren().add(vbox); display.getChildren().add(vbox);
} }
setGraphic(display);
// Set background depending on whether it is disabled or not // Set background depending on whether it is disabled or not
getStyleClass().add(chat.isDisabled() ? "disabled-chat" : "list-element"); getStyleClass().add(chat.isDisabled() ? "disabled-chat" : "list-element");
} }

View File

@ -91,9 +91,6 @@ public final class ChatScene implements EventListener, Restorable {
@FXML @FXML
private Label topBarStatusLabel; private Label topBarStatusLabel;
@FXML
private MenuItem deleteContactMenuItem;
@FXML @FXML
private ImageView attachmentView; private ImageView attachmentView;
@ -278,12 +275,13 @@ public final class ChatScene implements EventListener, Restorable {
@Event @Event
private void onGroupResize(GroupResize resize) { private void onGroupResize(GroupResize resize) {
final var chatFound = localDB.getChat(resize.getGroupID()); final var chatFound = localDB.getChat(resize.getGroupID());
if (chatFound.isEmpty()) return;
Platform.runLater(() -> { Platform.runLater(() -> {
chatList.refresh(); chatList.refresh();
// Update the top-bar status label if all conditions apply // Update the top-bar status label if all conditions apply
if (currentChat != null && currentChat.getRecipient().equals(chatFound.get().getRecipient())) if (currentChat != null && currentChat.getRecipient().equals(chatFound.get().getRecipient()))
chatFound.ifPresent(chat -> topBarStatusLabel.setText(chat.getRecipient().getContacts().size() + " members")); topBarStatusLabel.setText(chatFound.get().getRecipient().getContacts().size() + " members");
}); });
} }
@ -348,7 +346,6 @@ public final class ChatScene implements EventListener, Restorable {
final var scrollIndex = messageList.getItems().size() - currentChat.getUnreadAmount(); final var scrollIndex = messageList.getItems().size() - currentChat.getUnreadAmount();
messageList.scrollTo(scrollIndex); messageList.scrollTo(scrollIndex);
logger.log(Level.FINEST, "Loading chat with " + user + " at index " + scrollIndex); logger.log(Level.FINEST, "Loading chat with " + user + " at index " + scrollIndex);
deleteContactMenuItem.setText("Delete " + user.getName());
// Read the current chat // Read the current chat
currentChat.read(writeProxy); currentChat.read(writeProxy);
@ -746,18 +743,6 @@ public final class ChatScene implements EventListener, Restorable {
} }
} }
// Context menu actions
@FXML
private void blockOrDeleteContact() {
final var selectedChat = chatList.getSelectionModel().getSelectedItem();
if (selectedChat == null) return;
// If a contact has already been blocked deletes this chat else only blocks him
if (selectedChat.isDisabled()) UserUtil.deleteContact(selectedChat.getRecipient());
else UserUtil.blockContact(selectedChat.getRecipient());
}
/** /**
* Redesigns the UI when the {@link Chat} of the given contact has been marked * Redesigns the UI when the {@link Chat} of the given contact has been marked
* as disabled. * as disabled.

View File

@ -102,7 +102,7 @@ public final class UserUtil {
* @since Envoy Client v0.3-beta * @since Envoy Client v0.3-beta
*/ */
public static void deleteContact(Contact delete) { public static void deleteContact(Contact delete) {
if (!context.getClient().isOnline() || delete == null) return; if (delete == null) return;
else { else {
final var alert = new Alert(AlertType.CONFIRMATION); final var alert = new Alert(AlertType.CONFIRMATION);
alert.setContentText("Are you sure you want to delete " + delete.getName() alert.setContentText("Are you sure you want to delete " + delete.getName()

View File

@ -126,15 +126,6 @@
<ListView id="chat-list" fx:id="chatList" <ListView id="chat-list" fx:id="chatList"
focusTraversable="false" onMouseClicked="#chatListClicked" focusTraversable="false" onMouseClicked="#chatListClicked"
prefWidth="316.0" VBox.vgrow="ALWAYS"> prefWidth="316.0" VBox.vgrow="ALWAYS">
<contextMenu>
<ContextMenu anchorLocation="CONTENT_TOP_LEFT">
<items>
<MenuItem fx:id="deleteContactMenuItem"
mnemonicParsing="false" onAction="#blockOrDeleteContact"
text="Delete" />
</items>
</ContextMenu>
</contextMenu>
<padding> <padding>
<Insets bottom="5.0" left="5.0" right="2.0" top="5.0" /> <Insets bottom="5.0" left="5.0" right="2.0" top="5.0" />
</padding> </padding>

View File

@ -51,7 +51,7 @@ public final class Startup {
new UserStatusChangeProcessor(), new UserStatusChangeProcessor(),
new IDGeneratorRequestProcessor(), new IDGeneratorRequestProcessor(),
new UserSearchProcessor(), new UserSearchProcessor(),
new ContactOperationProcessor(), new UserOperationProcessor(),
new IsTypingProcessor(), new IsTypingProcessor(),
new NameChangeProcessor(), new NameChangeProcessor(),
new ProfilePicChangeProcessor(), new ProfilePicChangeProcessor(),

View File

@ -2,11 +2,13 @@ package envoy.server.data;
import java.time.Instant; import java.time.Instant;
import java.util.List; import java.util.List;
import java.util.logging.Level;
import javax.persistence.*; import javax.persistence.*;
import envoy.data.User.UserStatus; import envoy.data.User.UserStatus;
import envoy.server.net.ConnectionManager; import envoy.server.net.ConnectionManager;
import envoy.util.EnvoyLog;
/** /**
* Contains operations used for persistence. * Contains operations used for persistence.
@ -240,17 +242,24 @@ public final class PersistenceManager {
* @since Envoy Server Standalone v0.1-alpha * @since Envoy Server Standalone v0.1-alpha
*/ */
public void addContactBidirectional(long contactID1, long contactID2) { public void addContactBidirectional(long contactID1, long contactID2) {
addContactBidirectional(getContactByID(contactID1), getContactByID(contactID2));
}
// Get users by ID /**
final var c1 = getContactByID(contactID1); * Adds a contact to the contact list of another contact and vice versa.
final var c2 = getContactByID(contactID2); *
* @param contact1 the first contact
* @param contact2 the second contact
* @since Envoy Server v0.3-beta
*/
public void addContactBidirectional(Contact contact1, Contact contact2) {
// Add users to each others contact list // Add users to each others contact list
c1.getContacts().add(c2); contact1.getContacts().add(contact2);
c2.getContacts().add(c1); contact2.getContacts().add(contact1);
// Synchronize changes with the database // Synchronize changes with the database
transaction(() -> { entityManager.merge(c1); entityManager.merge(c2); }); transaction(() -> { entityManager.merge(contact1); entityManager.merge(contact2); });
} }
/** /**
@ -261,17 +270,24 @@ public final class PersistenceManager {
* @since Envoy Server v0.3-beta * @since Envoy Server v0.3-beta
*/ */
public void removeContactBidirectional(long contactID1, long contactID2) { public void removeContactBidirectional(long contactID1, long contactID2) {
removeContactBidirectional(getContactByID(contactID1), getContactByID(contactID2));
}
// Get users by ID /**
final var c1 = getContactByID(contactID1); * Removes a contact from the contact list of another contact and vice versa.
final var c2 = getContactByID(contactID2); *
* @param contact1 the first contact
* @param contact2 the second contact
* @since Envoy Server v0.3-beta
*/
public void removeContactBidirectional(Contact contact1, Contact contact2) {
// Remove users from each others contact list // Remove users from each others contact list
c1.getContacts().remove(c2); contact1.getContacts().remove(contact2);
c2.getContacts().remove(c1); contact2.getContacts().remove(contact1);
// Synchronize changes with the database // Synchronize changes with the database
transaction(() -> { entityManager.merge(c1); entityManager.merge(c2); }); transaction(() -> { entityManager.merge(contact1); entityManager.merge(contact2); });
} }
/** /**
@ -311,6 +327,14 @@ public final class PersistenceManager {
entityManagerRelatedAction.run(); entityManagerRelatedAction.run();
transaction.commit(); transaction.commit();
} }
} catch (final RollbackException e2) {
// Apparently a major problem exists. Discard faulty transaction and then go on.
if (transaction.isActive()) {
transaction.rollback();
EnvoyLog.getLogger(PersistenceManager.class)
.log(Level.SEVERE, "Could not perform transaction, hence discarding it. It's likely that a serious issue exists.");
} else throw e2;
} }
} }
} }

View File

@ -5,7 +5,7 @@ import static envoy.server.Startup.config;
import java.util.HashSet; import java.util.HashSet;
import envoy.event.*; import envoy.event.*;
import envoy.server.data.*; import envoy.server.data.PersistenceManager;
import envoy.server.net.*; import envoy.server.net.*;
/** /**
@ -27,18 +27,12 @@ public final class GroupCreationProcessor implements ObjectProcessor<GroupCreati
final envoy.server.data.Group group = new envoy.server.data.Group(); final envoy.server.data.Group group = new envoy.server.data.Group();
group.setName(groupCreation.get()); group.setName(groupCreation.get());
group.setContacts(new HashSet<>()); group.setContacts(new HashSet<>());
groupCreation.getInitialMemberIDs().stream().map(persistenceManager::getUserByID).forEach(group.getContacts()::add);
group.getContacts().add(persistenceManager.getContactByID(connectionManager.getUserIDBySocketID(socketID)));
group.getContacts().forEach(c -> c.getContacts().add(group));
group.getContacts().add(persistenceManager.getUserByID(connectionManager.getUserIDBySocketID(socketID)));
persistenceManager.addContact(group); persistenceManager.addContact(group);
final var resultGroup = group.toCommon(); groupCreation.getInitialMemberIDs()
group.getContacts()
.stream() .stream()
.map(Contact::getID) .map(persistenceManager::getUserByID)
.filter(connectionManager::isOnline) .forEach(member -> persistenceManager.addContactBidirectional(member, group));
.map(connectionManager::getSocketID) persistenceManager.addContactBidirectional(persistenceManager.getUserByID(connectionManager.getUserIDBySocketID(socketID)), group);
.forEach(memberSocketID -> writeProxy.write(memberSocketID, new GroupCreationResult(resultGroup))); writeProxy.writeToOnlineContacts(group.getContacts(), new GroupCreationResult(group.toCommon()));
writeProxy.write(socketID, new GroupCreationResult(resultGroup));
} }
} }

View File

@ -5,7 +5,7 @@ import java.util.logging.*;
import envoy.event.ElementOperation; import envoy.event.ElementOperation;
import envoy.event.contact.UserOperation; import envoy.event.contact.UserOperation;
import envoy.server.data.*; import envoy.server.data.PersistenceManager;
import envoy.server.net.*; import envoy.server.net.*;
import envoy.util.EnvoyLog; import envoy.util.EnvoyLog;
@ -13,10 +13,10 @@ import envoy.util.EnvoyLog;
* @author Kai S. K. Engelbart * @author Kai S. K. Engelbart
* @since Envoy Server Standalone v0.1-alpha * @since Envoy Server Standalone v0.1-alpha
*/ */
public final class ContactOperationProcessor implements ObjectProcessor<UserOperation> { public final class UserOperationProcessor implements ObjectProcessor<UserOperation> {
private static final ConnectionManager connectionManager = ConnectionManager.getInstance(); private static final ConnectionManager connectionManager = ConnectionManager.getInstance();
private static final Logger logger = EnvoyLog.getLogger(ContactOperationProcessor.class); private static final Logger logger = EnvoyLog.getLogger(UserOperationProcessor.class);
private static final PersistenceManager persistenceManager = PersistenceManager.getInstance(); private static final PersistenceManager persistenceManager = PersistenceManager.getInstance();
@Override @Override
@ -38,35 +38,15 @@ public final class ContactOperationProcessor implements ObjectProcessor<UserOper
// Remove the relationships and notify sender if he logs in using another // Remove the relationships and notify sender if he logs in using another
// LocalDB // LocalDB
persistenceManager.removeContactBidirectional(userID, contactID); persistenceManager.removeContactBidirectional(userID, contactID);
final var contact = persistenceManager.getContactByID(contactID);
sender.setLatestContactDeletion(Instant.now()); sender.setLatestContactDeletion(Instant.now());
// The sender wants to remove a normal contact
if (contact instanceof User) {
// Notify the removed contact on next startup(s) of this deletion // Notify the removed contact on next startup(s) of this deletion
persistenceManager.getUserByID(contactID).setLatestContactDeletion(Instant.now()); persistenceManager.getUserByID(contactID).setLatestContactDeletion(Instant.now());
// Notify the removed contact if online // Notify the removed contact if online
if (connectionManager.isOnline(contactID)) if (connectionManager.isOnline(contactID))
writeProxy.write(connectionManager.getSocketID(contactID), new UserOperation(sender.toCommon(), ElementOperation.REMOVE)); writeProxy.write(connectionManager.getSocketID(contactID), new UserOperation(sender.toCommon(), ElementOperation.REMOVE));
} else {
// The sender wants to be removed from a Group
final var group = persistenceManager.getGroupByID(contactID);
// The group has no more members and hence will be deleted
if (group.getContacts().isEmpty()) persistenceManager.deleteContact(group);
else {
// Informing the other members
writeProxy.writeToOnlineContacts(group.getContacts(), new UserOperation(sender.toCommon(), ElementOperation.REMOVE));
group.getContacts().forEach(c -> ((User) c).setLatestContactDeletion(Instant.now()));
}
}
break; break;
default:
logger.warning(String.format("Received %s with an unsupported operation.", evt));
} }
} }
} }