Add Ability to Delete Messages Locally #70

Merged
delvh merged 6 commits from f/delete-messages into develop 2020-09-30 20:50:59 +02:00
3 changed files with 138 additions and 68 deletions
Showing only changes of commit 0be5d0e12a - Show all commits

View File

@ -1,8 +1,6 @@
package envoy.client.ui.control;
import java.awt.Toolkit;
import java.awt.datatransfer.StringSelection;
import java.io.*;
import java.io.ByteArrayInputStream;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Map;
@ -12,19 +10,14 @@ import javafx.geometry.*;
import javafx.scene.control.*;
import javafx.scene.image.*;
import javafx.scene.layout.*;
import javafx.stage.FileChooser;
import envoy.client.data.*;
import envoy.client.net.Client;
import envoy.client.ui.SceneContext;
import envoy.client.util.IconUtil;
import envoy.client.util.*;
import envoy.data.*;
import envoy.data.Message.MessageStatus;
import envoy.event.MessageDeletion;
import envoy.util.EnvoyLog;
import dev.kske.eventbus.EventBus;
/**
* This class transforms a single {@link Message} into a UI component.
*
@ -36,15 +29,13 @@ public final class MessageControl extends Label {
private final boolean ownMessage;
private final LocalDB localDB = context.getLocalDB();
private final SceneContext sceneContext = context.getSceneContext();
private final Client client = context.getClient();
private final LocalDB localDB = context.getLocalDB();
private final Client client = context.getClient();
private static final Context context = Context.getInstance();
private static final DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss")
.withZone(ZoneId.systemDefault());
private static final Map<MessageStatus, Image> statusImages = IconUtil.loadByEnum(MessageStatus.class, 16);
private static final Settings settings = Settings.getInstance();
private static final Logger logger = EnvoyLog.getLogger(MessageControl.class);
/**
@ -79,14 +70,16 @@ public final class MessageControl extends Label {
final var items = contextMenu.getItems();
// Copy message action
final var copyMenuItem = new MenuItem("Copy Text");
copyMenuItem.setOnAction(e -> copyMessageText(message.getText()));
items.add(copyMenuItem);
if (!message.getText().isEmpty()) {
final var copyMenuItem = new MenuItem("Copy Text");
copyMenuItem.setOnAction(e -> MessageUtil.copyMessageText(message));
items.add(copyMenuItem);
}
// Delete message - if own message - action
if (ownMessage && client.isOnline()) {
final var deleteMenuItem = new MenuItem("Delete");
deleteMenuItem.setOnAction(e -> deleteMessage(message));
deleteMenuItem.setOnAction(e -> MessageUtil.deleteMessage(message));
items.add(deleteMenuItem);
}
@ -96,12 +89,12 @@ public final class MessageControl extends Label {
// Forward menu item
final var forwardMenuItem = new MenuItem("Forward");
forwardMenuItem.setOnAction(e -> forwardMessage(message));
forwardMenuItem.setOnAction(e -> MessageUtil.forwardMessage(message));
items.add(forwardMenuItem);
// Quote menu item
final var quoteMenuItem = new MenuItem("Quote");
quoteMenuItem.setOnAction(e -> quoteMessage(message));
quoteMenuItem.setOnAction(e -> MessageUtil.quoteMessage(message));
items.add(quoteMenuItem);
}
@ -127,7 +120,7 @@ public final class MessageControl extends Label {
break;
}
final var saveAttachment = new MenuItem("Save attachment");
saveAttachment.setOnAction(e -> saveAttachment(message));
saveAttachment.setOnAction(e -> MessageUtil.saveAttachment(message));
items.add(saveAttachment);
}
// Creating the textLabel
@ -155,52 +148,8 @@ public final class MessageControl extends Label {
setGraphic(vbox);
}
// Context Menu actions
private void copyMessageText(String text) {
logger.log(Level.FINEST, "A copy of message text \"" + text + "\" was requested");
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(text), null);
}
private void deleteMessage(Message message) {
final var messageDeletionEvent = new MessageDeletion(message.getID());
messageDeletionEvent.setOwnEvent();
// 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);
}
private void forwardMessage(Message message) { logger.log(Level.FINEST, "message forwarding was requested for " + message); }
private void quoteMessage(Message message) { logger.log(Level.FINEST, "message quotation was requested for " + message); }
private void loadMessageInfoScene(Message message) { logger.log(Level.FINEST, "message info scene was requested for " + message); }
private void saveAttachment(Message message) {
File file;
final var fileName = message.getAttachment().getName();
final var downloadLocation = settings.getDownloadLocation();
// Show save file dialog, if the user did not opt-out
if (!settings.isDownloadSavedWithoutAsking()) {
final var fileChooser = new FileChooser();
fileChooser.setInitialFileName(fileName);
fileChooser.setInitialDirectory(downloadLocation);
file = fileChooser.showSaveDialog(sceneContext.getStage());
} else file = new File(downloadLocation, fileName);
// A file was selected
if (file != null) try (FileOutputStream 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) {
logger.log(Level.WARNING, "Could not save attachment of " + message + ": ", e);
}
}
/**
* @return whether the message stored by this {@code MessageControl} has been
* sent by this user of Envoy

View File

@ -312,9 +312,7 @@ public final class ChatScene implements EventListener, Restorable {
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();
});
if (message.isOwnEvent()) Platform.runLater(() -> { messageList.getSelectionModel().clearSelection(); });
}
/**
@ -345,6 +343,24 @@ public final class ChatScene implements EventListener, Restorable {
.setNumberOfArguments(0)
.setDescription("Opens the settings screen")
.build("settings");
// Copy text of selection initialization
builder.setAction(text -> {
final var selectedMessage = messageList.getSelectionModel().getSelectedItem();
if (selectedMessage != null) MessageUtil.copyMessageText(selectedMessage);
}).setNumberOfArguments(0).setDescription("Copies the text of the currently selected message").build("cp-s");
// Delete selection initialization
builder.setAction(text -> {
final var selectedMessage = messageList.getSelectionModel().getSelectedItem();
if (selectedMessage != null) MessageUtil.deleteMessage(selectedMessage);
}).setNumberOfArguments(0).setDescription("Deletes the currently selected message").build("del-s");
// Save attachment of selection initialization
builder.setAction(text -> {
final var selectedMessage = messageList.getSelectionModel().getSelectedItem();
if (selectedMessage != null && selectedMessage.hasAttachment()) MessageUtil.saveAttachment(selectedMessage);
}).setNumberOfArguments(0).setDescription("Copies the text of the currently selected message").build("save-a-s");
}
@Override

View File

@ -0,0 +1,105 @@
package envoy.client.util;
import java.awt.Toolkit;
import java.awt.datatransfer.StringSelection;
import java.io.*;
import java.util.logging.*;
import javafx.stage.FileChooser;
import envoy.client.data.*;
import envoy.data.Message;
import envoy.event.MessageDeletion;
import envoy.util.EnvoyLog;
import dev.kske.eventbus.EventBus;
/**
* Contains methods that are commonly used for {@link Message}s.
*
* @author Leon Hofmeister
* @since Envoy Client v0.3-beta
*/
public class MessageUtil {
private MessageUtil() {}
private static Logger logger = EnvoyLog.getLogger(MessageUtil.class);
/**
* Copies the text of the given message to the System Clipboard.
*
* @param message the message whose text to copy
* @since Envoy Client v0.3-beta
*/
public static void copyMessageText(Message message) {
logger.log(Level.FINEST, "A copy of message text \"" + message.getText() + "\" was requested");
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(message.getText()), null);
}
/**
* Deletes the given message.
*
* @param message the message to delete
* @since Envoy Client v0.3-beta
*/
public static void deleteMessage(Message message) {
final var messageDeletionEvent = new MessageDeletion(message.getID());
messageDeletionEvent.setOwnEvent();
// Removing the message locally
delvh marked this conversation as resolved Outdated
Outdated
Review

You can use instanceof here, which is faster and more readable. Also, consider to comment this line.

You can use `instanceof` here, which is faster and more readable. Also, consider to comment this line.
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);
}
/**
* Forwards the given message.
* Currently not implemented.
*
* @param message the message to forward
* @since Envoy Client v0.3-beta
*/
public static void forwardMessage(Message message) { logger.log(Level.FINEST, "message forwarding was requested for " + message); }
delvh marked this conversation as resolved Outdated
Outdated
Review

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.
*
* @param message the message to quote
* @since Envoy Client v0.3-beta
*/
public static void quoteMessage(Message message) { logger.log(Level.FINEST, "message quotation was requested for " + message); }
/**
* Saves the attachment of a message, if present.
*
* @param message the message whose attachment to save
* @throws IllegalStateException if no attachment is present in the message
* @since Envoy Client v0.3-beta
*/
public static void saveAttachment(Message message) {
if (!message.hasAttachment()) throw new IllegalStateException("Cannot save a non-existing attachment");
delvh marked this conversation as resolved Outdated
Outdated
Review

This would rather be an IllegalArgumentException.

This would rather be an `IllegalArgumentException`.
File file;
final var fileName = message.getAttachment().getName();
final var downloadLocation = Settings.getInstance().getDownloadLocation();
// Show save file dialog, if the user did not opt-out
if (!Settings.getInstance().isDownloadSavedWithoutAsking()) {
final var fileChooser = new FileChooser();
fileChooser.setInitialFileName(fileName);
fileChooser.setInitialDirectory(downloadLocation);
file = fileChooser.showSaveDialog(Context.getInstance().getSceneContext().getStage());
} else file = new File(downloadLocation, fileName);
// A file was selected
if (file != null) try (FileOutputStream 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) {
logger.log(Level.WARNING, "Could not save attachment of " + message + ": ", e);
}
}
}