Contact Deletion #97
@ -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");
|
||||||
}
|
}
|
||||||
|
@ -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());
|
||||||
delvh marked this conversation as resolved
|
|||||||
|
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.
|
||||||
|
@ -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()
|
||||||
|
@ -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>
|
||||||
|
@ -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(),
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
// Notify the removed contact on next startup(s) of this deletion
|
||||||
if (contact instanceof User) {
|
persistenceManager.getUserByID(contactID).setLatestContactDeletion(Instant.now());
|
||||||
|
|
||||||
// Notify the removed contact on next startup(s) of this deletion
|
// Notify the removed contact if online
|
||||||
persistenceManager.getUserByID(contactID).setLatestContactDeletion(Instant.now());
|
if (connectionManager.isOnline(contactID))
|
||||||
|
writeProxy.write(connectionManager.getSocketID(contactID), new UserOperation(sender.toCommon(), ElementOperation.REMOVE));
|
||||||
// Notify the removed contact if online
|
|
||||||
if (connectionManager.isOnline(contactID))
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user
Why not use
Optional#ifPresent
here?