Add Ability to Delete Messages Locally #70
20
client/src/main/java/envoy/client/event/MessageDeletion.java
Normal file
@ -0,0 +1,20 @@
|
||||
package envoy.client.event;
|
||||
|
||||
import envoy.event.Event;
|
||||
|
||||
/**
|
||||
* Conveys the deletion of a message between clients and server.
|
||||
delvh marked this conversation as resolved
Outdated
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Common v0.3-beta
|
||||
*/
|
||||
public class MessageDeletion extends Event<Long> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* @param messageID the ID of the deleted message
|
||||
* @since Envoy Common v0.3-beta
|
||||
*/
|
||||
public MessageDeletion(long messageID) { super(messageID); }
|
||||
}
|
@ -78,7 +78,7 @@ public final class MessageControl extends Label {
|
||||
|
||||
// Delete message - if own message - action
|
||||
if (ownMessage && client.isOnline()) {
|
||||
final var deleteMenuItem = new MenuItem("Delete");
|
||||
final var deleteMenuItem = new MenuItem("Delete locally");
|
||||
deleteMenuItem.setOnAction(e -> MessageUtil.deleteMessage(message));
|
||||
items.add(deleteMenuItem);
|
||||
}
|
||||
|
@ -82,6 +82,7 @@ public class TextInputContextMenu extends ContextMenu {
|
||||
copyMI.disableProperty().bind(control.selectedTextProperty().isEmpty());
|
||||
deleteMI.disableProperty().bind(control.selectedTextProperty().isEmpty());
|
||||
clearMI.disableProperty().bind(control.textProperty().isEmpty());
|
||||
selectAllMI.disableProperty().bind(control.textProperty().isEmpty());
|
||||
setOnShowing(e -> pasteMI.setDisable(!Clipboard.getSystemClipboard().hasString()));
|
||||
|
||||
selectAllMI.getProperties().put("refreshMenu", Boolean.TRUE);
|
||||
|
@ -223,7 +223,7 @@ public final class ChatScene implements EventListener, Restorable {
|
||||
// The sender of the message is the recipient of the chat
|
||||
// Exceptions: this user is the sender (sync) or group message (group is
|
||||
// recipient)
|
||||
final boolean ownMessage = message.getSenderID() == localDB.getUser().getID();
|
||||
final var ownMessage = message.getSenderID() == localDB.getUser().getID();
|
||||
delvh marked this conversation as resolved
kske
commented
I thought we don't declare primitives as I thought we don't declare primitives as `var`?
delvh
commented
We don't? We don't?
Well, we have to decide quick since I reactived `use var instad of field` in my formatter, and this was the result of it.
Should I deactivate it?
kske
commented
You can deactivate it, however I will publish a new version of the formatter soon. You can deactivate it, however I will publish a new version of the formatter soon.
delvh
commented
So, I don't have to change it? So, I don't have to change it?
kske
commented
It's up to you. With the new formatter, it will get changed anyway sooner or later. It's up to you. With the new formatter, it will get changed anyway sooner or later.
|
||||
final var recipientID = message instanceof GroupMessage || ownMessage ? message.getRecipientID() : message.getSenderID();
|
||||
|
||||
localDB.getChat(recipientID).ifPresent(chat -> {
|
||||
@ -308,13 +308,6 @@ public final class ChatScene implements EventListener, Restorable {
|
||||
@Event(eventType = Logout.class, priority = 200)
|
||||
private void onLogout() { eventBus.removeListener(this); }
|
||||
|
||||
@Event(priority = 200)
|
||||
private void onMessageDeletion(MessageDeletion message) {
|
||||
|
||||
// Clearing the selection if the own user was the sender of this event
|
||||
if (message.isOwnEvent()) Platform.runLater(() -> { messageList.getSelectionModel().clearSelection(); });
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes all {@code SystemCommands} used in {@code ChatScene}.
|
||||
*
|
||||
@ -412,7 +405,7 @@ public final class ChatScene implements EventListener, Restorable {
|
||||
if (currentChat != null) {
|
||||
topBarContactLabel.setText(currentChat.getRecipient().getName());
|
||||
if (currentChat.getRecipient() instanceof User) {
|
||||
final String status = ((User) currentChat.getRecipient()).getStatus().toString();
|
||||
final var status = ((User) currentChat.getRecipient()).getStatus().toString();
|
||||
topBarStatusLabel.setText(status);
|
||||
topBarStatusLabel.getStyleClass().add(status.toLowerCase());
|
||||
recipientProfilePic.setImage(IconUtil.loadIconThemeSensitive("user_icon", 43));
|
||||
@ -420,7 +413,7 @@ public final class ChatScene implements EventListener, Restorable {
|
||||
topBarStatusLabel.setText(currentChat.getRecipient().getContacts().size() + " members");
|
||||
recipientProfilePic.setImage(IconUtil.loadIconThemeSensitive("group_icon", 43));
|
||||
}
|
||||
final Rectangle clip = new Rectangle();
|
||||
final var clip = new Rectangle();
|
||||
clip.setWidth(43);
|
||||
clip.setHeight(43);
|
||||
clip.setArcHeight(43);
|
||||
@ -778,6 +771,13 @@ public final class ChatScene implements EventListener, Restorable {
|
||||
pendingAttachment = messageAttachment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the current message selection
|
||||
delvh marked this conversation as resolved
Outdated
kske
commented
Append a dot to the end of the sentence. Append a dot to the end of the sentence.
|
||||
*
|
||||
* @since Envoy Client v0.3-beta
|
||||
*/
|
||||
public void clearMessageSelection() { messageList.getSelectionModel().clearSelection(); }
|
||||
|
||||
@FXML
|
||||
private void searchContacts() {
|
||||
chats.setPredicate(contactSearch.getText().isBlank() ? c -> true
|
||||
|
@ -8,8 +8,9 @@ import java.util.logging.*;
|
||||
import javafx.stage.FileChooser;
|
||||
|
||||
import envoy.client.data.*;
|
||||
import envoy.client.event.MessageDeletion;
|
||||
import envoy.client.ui.controller.ChatScene;
|
||||
import envoy.data.Message;
|
||||
import envoy.event.MessageDeletion;
|
||||
import envoy.util.EnvoyLog;
|
||||
|
||||
import dev.kske.eventbus.EventBus;
|
||||
@ -45,13 +46,12 @@ public class MessageUtil {
|
||||
*/
|
||||
public static void deleteMessage(Message message) {
|
||||
final var messageDeletionEvent = new MessageDeletion(message.getID());
|
||||
messageDeletionEvent.setOwnEvent();
|
||||
final var controller = Context.getInstance().getSceneContext().getController();
|
||||
if (controller.getClass().equals(ChatScene.class)) ((ChatScene) controller).clearMessageSelection();
|
||||
delvh marked this conversation as resolved
Outdated
kske
commented
You can use You can use `instanceof` here, which is faster and more readable. Also, consider to comment this line.
|
||||
|
||||
// Removing the message locally
|
||||
EventBus.getInstance().dispatch(messageDeletionEvent);
|
||||
|
||||
// Removing the message on the server and this chat's recipients
|
||||
Context.getInstance().getClient().send(messageDeletionEvent);
|
||||
logger.log(Level.FINEST, "message deletion was requested for " + message);
|
||||
}
|
||||
|
||||
@ -64,7 +64,7 @@ public class MessageUtil {
|
||||
*/
|
||||
public static void forwardMessage(Message message) { logger.log(Level.FINEST, "message forwarding was requested for " + message); }
|
||||
delvh marked this conversation as resolved
Outdated
kske
commented
Why are we adding context menu actions and methods that are not implemented on a branch that has nothing to do with it? By the way, most logger statements start with a capital letter, so if you decide to keep the method, please consider that. Why are we adding context menu actions and methods that are not implemented on a branch that has nothing to do with it?
By the way, most logger statements start with a capital letter, so if you decide to keep the method, please consider that.
|
||||
|
||||
/**selected
|
||||
/**
|
||||
* Quotes the given message.
|
||||
* Currently not implemented.
|
||||
*
|
||||
@ -95,7 +95,7 @@ public class MessageUtil {
|
||||
} else file = new File(downloadLocation, fileName);
|
||||
|
||||
// A file was selected
|
||||
if (file != null) try (FileOutputStream fos = new FileOutputStream(file)) {
|
||||
if (file != null) try (var fos = new FileOutputStream(file)) {
|
||||
fos.write(message.getAttachment().getData());
|
||||
logger.log(Level.FINE, "Attachment of message was saved at " + file.getAbsolutePath());
|
||||
} catch (final IOException e) {
|
||||
|
@ -1,34 +0,0 @@
|
||||
package envoy.event;
|
||||
|
||||
/**
|
||||
* Conveys the deletion of a message between clients and server.
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Common v0.3-beta
|
||||
*/
|
||||
public class MessageDeletion extends Event<Long> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private transient boolean ownEvent;
|
||||
|
||||
/**
|
||||
* @param messageID the ID of the deleted message
|
||||
* @since Envoy Common v0.3-beta
|
||||
*/
|
||||
public MessageDeletion(long messageID) { super(messageID); }
|
||||
|
||||
/**
|
||||
* @return whether the current user was the creator of this event.
|
||||
* @since Envoy Common v0.3-beta
|
||||
*/
|
||||
public boolean isOwnEvent() { return ownEvent; }
|
||||
|
||||
/**
|
||||
* Marks this event as being sent by this user. Is needed for a bug free
|
||||
* and efficient selection clearing.
|
||||
*
|
||||
* @since Envoy Common v0.3-beta
|
||||
*/
|
||||
public void setOwnEvent() { ownEvent = true; }
|
||||
}
|
@ -56,8 +56,7 @@ public final class Startup {
|
||||
new NameChangeProcessor(),
|
||||
new ProfilePicChangeProcessor(),
|
||||
new PasswordChangeRequestProcessor(),
|
||||
new IssueProposalProcessor(),
|
||||
new MessageDeletionProcessor())));
|
||||
new IssueProposalProcessor())));
|
||||
|
||||
// Initialize the current message ID
|
||||
final var persistenceManager = PersistenceManager.getInstance();
|
||||
|
@ -1,7 +1,7 @@
|
||||
package envoy.server.data;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
@ -98,34 +98,6 @@ public abstract class Contact {
|
||||
*/
|
||||
public void setCreationDate(Instant creationDate) { this.creationDate = creationDate; }
|
||||
|
||||
/**
|
||||
* Shortcut to convert a {@code Contact} into a {@code User}.
|
||||
*
|
||||
* @param contact the contact to convert
|
||||
* @return the casted contact
|
||||
* @throws IllegalStateException if the given contact is not a User
|
||||
* @since Envoy Server v0.3-beta
|
||||
*/
|
||||
public static User toUser(Contact contact) {
|
||||
if (!(contact instanceof User)) throw new IllegalStateException("Cannot cast a non user to a user");
|
||||
return (User) contact;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut to convert a set of {@code Contact}s into a set of {@code User}s.
|
||||
*
|
||||
* @param contacts the contacts to convert
|
||||
* @return the casted contacts
|
||||
* @throws IllegalStateException if one of the given contacts is not a User
|
||||
* @since Envoy Server v0.3-beta
|
||||
*/
|
||||
public static Set<User> toUser(Set<Contact> contacts) {
|
||||
final var newSet = new HashSet<User>();
|
||||
for (final var contact : contacts)
|
||||
newSet.add(toUser(contact));
|
||||
return newSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() { return String.format("%s[id=%d,name=%s,%d contact(s)]", getClass().getSimpleName(), id, name, contacts.size()); }
|
||||
}
|
||||
|
@ -1,81 +0,0 @@
|
||||
package envoy.server.data;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
/**
|
||||
* Defines a message that has been deleted.
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Server v0.3-beta
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "deletionEvents")
|
||||
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
|
||||
public final class MessageDeletion {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
protected long messageID;
|
||||
|
||||
@ManyToOne(targetEntity = User.class)
|
||||
protected Set<User> recipientsToInform;
|
||||
|
||||
/**
|
||||
* Creates an instance of {@code DeletionEvent}.
|
||||
*
|
||||
* @since Envoy Server v0.3-beta
|
||||
*/
|
||||
public MessageDeletion() {}
|
||||
|
||||
/**
|
||||
* Creates an instance of {@code MessageDeletion}.
|
||||
*
|
||||
* @param messageID the ID of the message
|
||||
* @param recipientsToInform the recipientsToInform of the message<br>
|
||||
* <strong>that have not yet been notified of its
|
||||
* deletion</strong>
|
||||
* @since Envoy Server v0.3-beta
|
||||
*/
|
||||
public MessageDeletion(long messageID, Set<User> recipientsToInform) {
|
||||
this.messageID = messageID;
|
||||
this.recipientsToInform = recipientsToInform;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the messageID
|
||||
* @since Envoy Server v0.3-beta
|
||||
*/
|
||||
public long getMessageID() { return messageID; }
|
||||
|
||||
/**
|
||||
* @param messageID the messageID to set
|
||||
* @since Envoy Server v0.3-beta
|
||||
*/
|
||||
public void setMessageID(long messageID) { this.messageID = messageID; }
|
||||
|
||||
/**
|
||||
* @return the recipients that have yet to be informed
|
||||
* @since Envoy Server v0.3-beta
|
||||
*/
|
||||
public Set<User> getRecipientsToInform() { return recipientsToInform; }
|
||||
|
||||
/**
|
||||
* @param recipientsToInform the recipients that have yet to be informed
|
||||
* @since Envoy Server v0.3-beta
|
||||
*/
|
||||
public void setRecipientsToInform(Set<User> recipientsToInform) { this.recipientsToInform = recipientsToInform; }
|
||||
|
||||
/**
|
||||
* @param user the user who has been informed of the message deletion
|
||||
* @since Envoy Server v0.3-beta
|
||||
*/
|
||||
public void recipientInformed(User user) { recipientsToInform.remove(user); }
|
||||
|
||||
/**
|
||||
* @param users the users that have been informed of the message deletion
|
||||
* @since Envoy Server v0.3-beta
|
||||
*/
|
||||
public void recipientInformed(Collection<User> users) { recipientsToInform.removeAll(users); }
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package envoy.server.data;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
@ -9,6 +9,8 @@ import envoy.data.User.UserStatus;
|
||||
import envoy.server.net.ConnectionManager;
|
||||
|
||||
/**
|
||||
* Contains operations used for data retrieval.
|
||||
delvh marked this conversation as resolved
Outdated
kske
commented
As its not only retrieval, but also storage, maybe change this to "... for persistence." As its not only retrieval, but also storage, maybe change this to "... for persistence."
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @author Maximilian Käfer
|
||||
* @since Envoy Server Standalone v0.1-alpha
|
||||
@ -100,35 +102,12 @@ public final class PersistenceManager {
|
||||
public void deleteContact(Contact contact) { remove(contact); }
|
||||
|
||||
/**
|
||||
* Deletes a {@link Message} in the database and creates a new
|
||||
* {@link MessageDeletion} object for <strong>all</strong> recipients of the
|
||||
* message.
|
||||
* Deletes a {@link Message} in the database.
|
||||
*
|
||||
* @param message the {@link Message} to delete
|
||||
* @return the created {@link MessageDeletion} object
|
||||
* @since Envoy Server v0.3-beta
|
||||
* @since Envoy Server Standalone v0.1-alpha
|
||||
*/
|
||||
public MessageDeletion deleteMessage(Message message) {
|
||||
final var recipient = message.getRecipient();
|
||||
return deleteMessage(message,
|
||||
recipient instanceof Group ? Contact.toUser(getGroupByID(recipient.id).getContacts()) : Set.of(Contact.toUser(recipient)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a {@link Message} in the database and creates a new
|
||||
* {@link MessageDeletion} object for the given recipients of the message.
|
||||
*
|
||||
* @param message the {@link Message} to delete
|
||||
* @param recipientsYetToInform the (sub)set of all recipients of that message
|
||||
* @return the created {@link MessageDeletion} object
|
||||
* @since Envoy Server v0.3-beta
|
||||
*/
|
||||
public MessageDeletion deleteMessage(Message message, Set<User> recipientsYetToInform) {
|
||||
final MessageDeletion deletion = new MessageDeletion(message.id, recipientsYetToInform);
|
||||
persist(deletion);
|
||||
remove(message);
|
||||
return deletion;
|
||||
}
|
||||
public void deleteMessage(Message message) { remove(message); }
|
||||
|
||||
/**
|
||||
* Searches for a {@link User} with a specific ID.
|
||||
@ -195,16 +174,6 @@ public final class PersistenceManager {
|
||||
*/
|
||||
public ConfigItem getConfigItemByID(String key) { return entityManager.find(ConfigItem.class, key); }
|
||||
|
||||
/**
|
||||
* Searches for a {@link MessageDeletion} with the given message id.
|
||||
*
|
||||
* @param id the id of the message to search for
|
||||
* @return the message deletion object with the specified ID or {@code null} if
|
||||
* none is found
|
||||
* @since Envoy Server v0.3-beta
|
||||
*/
|
||||
public MessageDeletion getMessageDeletionByID(long id) { return entityManager.find(MessageDeletion.class, id); }
|
||||
|
||||
/**
|
||||
* Returns all messages received while being offline or the ones that have
|
||||
* changed.
|
||||
|
@ -1,18 +0,0 @@
|
||||
package envoy.server.processors;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import envoy.event.MessageDeletion;
|
||||
import envoy.server.net.ObjectWriteProxy;
|
||||
|
||||
/**
|
||||
* Listens for and handles incoming {@link MessageDeletion}s.
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Server v0.3-beta
|
||||
*/
|
||||
public class MessageDeletionProcessor implements ObjectProcessor<MessageDeletion> {
|
||||
|
||||
@Override
|
||||
public void process(MessageDeletion message, long socketID, ObjectWriteProxy writeProxy) throws IOException {}
|
||||
}
|
This does not make sense as this is a client-sided event.
That's an artifact from when it was an Event in Common.