From 8bdd201b289b2cf8752f07e9acee9f999274c8ec Mon Sep 17 00:00:00 2001 From: delvh Date: Mon, 19 Oct 2020 22:01:43 +0200 Subject: [PATCH 1/3] Add Ctrl+U and Ctrl+K shortcuts to ChatScene --- .../envoy/client/ui/controller/ChatScene.java | 37 +++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/client/src/main/java/envoy/client/ui/controller/ChatScene.java b/client/src/main/java/envoy/client/ui/controller/ChatScene.java index 2242d25..81b8c9a 100644 --- a/client/src/main/java/envoy/client/ui/controller/ChatScene.java +++ b/client/src/main/java/envoy/client/ui/controller/ChatScene.java @@ -6,6 +6,7 @@ import java.io.*; import java.nio.file.Files; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.util.*; import java.util.logging.*; import javafx.animation.RotateTransition; @@ -24,19 +25,21 @@ import javafx.scene.shape.Rectangle; import javafx.stage.FileChooser; import javafx.util.Duration; -import dev.kske.eventbus.*; import dev.kske.eventbus.Event; +import dev.kske.eventbus.EventBus; +import dev.kske.eventbus.EventListener; import envoy.data.*; import envoy.data.Attachment.AttachmentType; import envoy.data.Message.MessageStatus; import envoy.event.*; -import envoy.event.contact.UserOperation; +import envoy.event.contact.*; import envoy.exception.EnvoyException; import envoy.util.EnvoyLog; import envoy.client.data.*; import envoy.client.data.audio.AudioRecorder; +import envoy.client.data.shortcuts.KeyboardMapping; import envoy.client.event.*; import envoy.client.net.*; import envoy.client.ui.*; @@ -51,7 +54,7 @@ import envoy.client.util.*; * @author Kai S. K. Engelbart * @since Envoy Client v0.1-beta */ -public final class ChatScene implements EventListener, Restorable { +public final class ChatScene implements EventListener, Restorable, KeyboardMapping { @FXML private ListView messageList; @@ -346,6 +349,11 @@ public final class ChatScene implements EventListener, Restorable { eventBus.removeListener(this); } + @Event(eventType = AccountDeletion.class) + private void onAccountDeletion() { + Platform.runLater(chatList::refresh); + } + @Override public void onRestore() { updateRemainingCharsLabel(); @@ -871,4 +879,27 @@ public final class ChatScene implements EventListener, Restorable { : c -> c.getRecipient().getName().toLowerCase() .contains(contactSearch.getText().toLowerCase())); } + + @Override + public Map getKeyboardShortcuts() { + final var map = new HashMap(); + + // Delete text before the caret with "Control" + U + map.put(new KeyCodeCombination(KeyCode.U, KeyCombination.CONTROL_DOWN), () -> { + messageTextArea + .setText(messageTextArea.getText().substring(messageTextArea.getCaretPosition())); + checkPostConditions(false); + }); + + // Delete text after the caret with "Control" + K + map.put(new KeyCodeCombination(KeyCode.K, KeyCombination.CONTROL_DOWN), () -> { + messageTextArea + .setText( + messageTextArea.getText().substring(0, messageTextArea.getCaretPosition())); + checkPostConditions(false); + messageTextArea.positionCaret(messageTextArea.getText().length()); + }); + + return map; + } } -- 2.30.2 From f67ca1d61de6c22f460e8ff6eeb3f631aa42fdb1 Mon Sep 17 00:00:00 2001 From: delvh Date: Mon, 19 Oct 2020 22:16:18 +0200 Subject: [PATCH 2/3] Add option to delete your account --- .../src/main/java/envoy/client/data/Chat.java | 11 ++--- .../main/java/envoy/client/data/LocalDB.java | 29 ++++++++++++++ .../envoy/client/ui/controller/ChatScene.java | 37 ++++++++--------- .../ui/controller/GroupCreationTab.java | 8 +++- .../client/ui/settings/UserSettingsPane.java | 31 +++++++++----- .../main/java/envoy/client/util/UserUtil.java | 40 ++++++++++++++++++- .../envoy/event/contact/AccountDeletion.java | 22 ++++++++++ .../src/main/java/envoy/server/Startup.java | 3 +- .../main/java/envoy/server/data/Message.java | 27 +++++++++---- .../envoy/server/data/PersistenceManager.java | 4 ++ .../envoy/server/net/ConnectionManager.java | 6 ++- .../processors/AccountDeletionProcessor.java | 33 +++++++++++++++ .../processors/GroupResizeProcessor.java | 7 ++++ .../processors/UserOperationProcessor.java | 18 ++++++--- 14 files changed, 221 insertions(+), 55 deletions(-) create mode 100644 common/src/main/java/envoy/event/contact/AccountDeletion.java create mode 100644 server/src/main/java/envoy/server/processors/AccountDeletionProcessor.java diff --git a/client/src/main/java/envoy/client/data/Chat.java b/client/src/main/java/envoy/client/data/Chat.java index 892b5ca..c62fd63 100644 --- a/client/src/main/java/envoy/client/data/Chat.java +++ b/client/src/main/java/envoy/client/data/Chat.java @@ -22,20 +22,21 @@ import envoy.client.net.WriteProxy; */ public class Chat implements Serializable { - protected boolean disabled; + protected transient ObservableList messages = FXCollections.observableArrayList(); + + protected final Contact recipient; + + protected boolean disabled; + protected boolean underlyingContactDeleted; /** * Stores the last time an {@link envoy.event.IsTyping} event has been sent. */ protected transient long lastWritingEvent; - protected transient ObservableList messages = FXCollections.observableArrayList(); - protected int unreadAmount; protected static IntegerProperty totalUnreadAmount = new SimpleIntegerProperty(); - protected final Contact recipient; - private static final long serialVersionUID = 2L; /** diff --git a/client/src/main/java/envoy/client/data/LocalDB.java b/client/src/main/java/envoy/client/data/LocalDB.java index 2ae3570..547c9a0 100644 --- a/client/src/main/java/envoy/client/data/LocalDB.java +++ b/client/src/main/java/envoy/client/data/LocalDB.java @@ -248,6 +248,10 @@ public final class LocalDB implements EventListener { */ @Event(eventType = EnvoyCloseEvent.class, priority = 500) private synchronized void save() { + + // Stop saving if this account has been deleted + if (userFile == null) + return; EnvoyLog.getLogger(LocalDB.class).log(Level.FINER, "Saving local database..."); // Save users @@ -273,6 +277,20 @@ public final class LocalDB implements EventListener { } } + /** + * Deletes any local remnant of this user. + * + * @since Envoy Client v0.3-beta + */ + public void delete() { + if (lastLoginFile != null) + lastLoginFile.delete(); + userFile.delete(); + users.remove(user.getName()); + userFile = null; + onLogout(); + } + @Event(priority = 500) private void onMessage(Message msg) { if (msg.getStatus() == MessageStatus.SENT) @@ -404,6 +422,17 @@ public final class LocalDB implements EventListener { getChat(event.get().getID()).ifPresent(chat -> chat.setDisabled(true)); } + @Event(priority = 500) + private void onAccountDeletion(AccountDeletion deletion) { + if (user.getID() == deletion.get()) + logger.log(Level.WARNING, + "I have been informed by the server that I have been deleted without even knowing it..."); + getChat(deletion.get()).ifPresent(chat -> { + chat.setDisabled(true); + chat.setUnderlyingContactDeleted(true); + }); + } + /** * @return a {@code Map} of all users stored locally with their user names as keys * @since Envoy Client v0.2-alpha diff --git a/client/src/main/java/envoy/client/ui/controller/ChatScene.java b/client/src/main/java/envoy/client/ui/controller/ChatScene.java index 81b8c9a..8dc48ef 100644 --- a/client/src/main/java/envoy/client/ui/controller/ChatScene.java +++ b/client/src/main/java/envoy/client/ui/controller/ChatScene.java @@ -6,7 +6,7 @@ import java.io.*; import java.nio.file.Files; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; -import java.util.*; +import java.util.Map; import java.util.logging.*; import javafx.animation.RotateTransition; @@ -25,9 +25,8 @@ import javafx.scene.shape.Rectangle; import javafx.stage.FileChooser; import javafx.util.Duration; +import dev.kske.eventbus.*; import dev.kske.eventbus.Event; -import dev.kske.eventbus.EventBus; -import dev.kske.eventbus.EventListener; import envoy.data.*; import envoy.data.Attachment.AttachmentType; @@ -882,24 +881,22 @@ public final class ChatScene implements EventListener, Restorable, KeyboardMappi @Override public Map getKeyboardShortcuts() { - final var map = new HashMap(); + return Map.of( - // Delete text before the caret with "Control" + U - map.put(new KeyCodeCombination(KeyCode.U, KeyCombination.CONTROL_DOWN), () -> { - messageTextArea - .setText(messageTextArea.getText().substring(messageTextArea.getCaretPosition())); - checkPostConditions(false); - }); + // Delete text before the caret with "Control" + U + new KeyCodeCombination(KeyCode.U, KeyCombination.CONTROL_DOWN), () -> { + messageTextArea + .setText( + messageTextArea.getText().substring(messageTextArea.getCaretPosition())); + checkPostConditions(false); - // Delete text after the caret with "Control" + K - map.put(new KeyCodeCombination(KeyCode.K, KeyCombination.CONTROL_DOWN), () -> { - messageTextArea - .setText( - messageTextArea.getText().substring(0, messageTextArea.getCaretPosition())); - checkPostConditions(false); - messageTextArea.positionCaret(messageTextArea.getText().length()); - }); - - return map; + // Delete text after the caret with "Control" + K + }, new KeyCodeCombination(KeyCode.K, KeyCombination.CONTROL_DOWN), () -> { + messageTextArea + .setText( + messageTextArea.getText().substring(0, messageTextArea.getCaretPosition())); + checkPostConditions(false); + messageTextArea.positionCaret(messageTextArea.getText().length()); + }); } } diff --git a/client/src/main/java/envoy/client/ui/controller/GroupCreationTab.java b/client/src/main/java/envoy/client/ui/controller/GroupCreationTab.java index 39f414d..af5c054 100644 --- a/client/src/main/java/envoy/client/ui/controller/GroupCreationTab.java +++ b/client/src/main/java/envoy/client/ui/controller/GroupCreationTab.java @@ -14,7 +14,7 @@ import dev.kske.eventbus.*; import envoy.data.*; import envoy.event.GroupCreation; -import envoy.event.contact.UserOperation; +import envoy.event.contact.*; import envoy.util.Bounds; import envoy.client.data.*; @@ -252,4 +252,10 @@ public class GroupCreationTab implements EventListener { } }); } + + @Event + private void onAccountDeletion(AccountDeletion deletion) { + final var deletedID = deletion.get(); + Platform.runLater(() -> userList.getItems().removeIf(user -> (user.getID() == deletedID))); + } } diff --git a/client/src/main/java/envoy/client/ui/settings/UserSettingsPane.java b/client/src/main/java/envoy/client/ui/settings/UserSettingsPane.java index c3790a2..aa194c5 100644 --- a/client/src/main/java/envoy/client/ui/settings/UserSettingsPane.java +++ b/client/src/main/java/envoy/client/ui/settings/UserSettingsPane.java @@ -20,7 +20,7 @@ import envoy.event.*; import envoy.util.*; import envoy.client.ui.control.ProfilePicImageView; -import envoy.client.util.IconUtil; +import envoy.client.util.*; /** * @author Leon Hofmeister @@ -38,6 +38,7 @@ public final class UserSettingsPane extends OnlineOnlySettingsPane { private final PasswordField newPasswordField = new PasswordField(); private final PasswordField repeatNewPasswordField = new PasswordField(); private final Button saveButton = new Button("Save"); + private final Button deleteAccountButton = new Button("Delete Account"); private static final EventBus eventBus = EventBus.getInstance(); private static final Logger logger = EnvoyLog.getLogger(UserSettingsPane.class); @@ -112,16 +113,19 @@ public final class UserSettingsPane extends OnlineOnlySettingsPane { final PasswordField[] passwordFields = { currentPasswordField, newPasswordField, repeatNewPasswordField }; - final EventHandler passwordEntered = e -> { - newPassword = - newPasswordField.getText(); - validPassword = newPassword - .equals( - repeatNewPasswordField - .getText()) - && !newPasswordField - .getText().isBlank(); - }; + final EventHandler passwordEntered = + e -> { + newPassword = + newPasswordField + .getText(); + validPassword = + newPassword.equals( + repeatNewPasswordField + .getText()) + && !newPasswordField + .getText() + .isBlank(); + }; newPasswordField.setOnInputMethodTextChanged(passwordEntered); newPasswordField.setOnKeyTyped(passwordEntered); repeatNewPasswordField.setOnInputMethodTextChanged(passwordEntered); @@ -140,6 +144,11 @@ public final class UserSettingsPane extends OnlineOnlySettingsPane { .setOnAction(e -> save(currentPasswordField.getText())); saveButton.setAlignment(Pos.BOTTOM_RIGHT); getChildren().add(saveButton); + + // Displaying the delete account button + deleteAccountButton.setAlignment(Pos.BASELINE_CENTER); + deleteAccountButton.setOnAction(e -> UserUtil.deleteAccount()); + getChildren().add(deleteAccountButton); } /** diff --git a/client/src/main/java/envoy/client/util/UserUtil.java b/client/src/main/java/envoy/client/util/UserUtil.java index 27078cc..aba9557 100644 --- a/client/src/main/java/envoy/client/util/UserUtil.java +++ b/client/src/main/java/envoy/client/util/UserUtil.java @@ -2,7 +2,7 @@ package envoy.client.util; import java.util.logging.*; -import javafx.scene.control.Alert; +import javafx.scene.control.*; import javafx.scene.control.Alert.AlertType; import dev.kske.eventbus.EventBus; @@ -10,7 +10,7 @@ import dev.kske.eventbus.EventBus; import envoy.data.*; import envoy.data.User.UserStatus; import envoy.event.*; -import envoy.event.contact.UserOperation; +import envoy.event.contact.*; import envoy.util.EnvoyLog; import envoy.client.data.Context; @@ -121,4 +121,40 @@ public final class UserUtil { }); } } + + /** + * Deletes anything pointing to this user, independent of client or server. Will do nothing if + * the client is currently offline. + * + * @since Envoy Client v0.3-beta + */ + public static void deleteAccount() { + if (!context.getClient().isOnline()) + return; + else { + + // Show the first wall of defense, if not disabled by the user + final var outerAlert = new Alert(AlertType.CONFIRMATION); + outerAlert.setContentText( + "Are you sure you want to delete your account entirely? This action can seriously not be undone."); + outerAlert.setTitle("Delete Account?"); + AlertHelper.confirmAction(outerAlert, () -> { + + // Show the final wall of defense in every case + final var lastAlert = new Alert(AlertType.WARNING, + "Do you REALLY want to delete your account? Last Warning. Proceed?", + ButtonType.CANCEL, ButtonType.OK); + lastAlert.setTitle("Delete Account?"); + lastAlert.showAndWait().filter(ButtonType.OK::equals).ifPresent(b2 -> { + + // Delete the account + context.getClient() + .send(new AccountDeletion(context.getLocalDB().getUser().getID())); + context.getLocalDB().delete(); + logger.log(Level.INFO, "The user just deleted his account. Goodbye."); + ShutdownHelper.exit(true); + }); + }); + } + } } diff --git a/common/src/main/java/envoy/event/contact/AccountDeletion.java b/common/src/main/java/envoy/event/contact/AccountDeletion.java new file mode 100644 index 0000000..dabc0be --- /dev/null +++ b/common/src/main/java/envoy/event/contact/AccountDeletion.java @@ -0,0 +1,22 @@ +package envoy.event.contact; + +import envoy.event.Event; + +/** + * Signifies the deletion of an account. + * + * @author Leon Hofmeister + * @since Envoy Common v0.3-beta + */ +public class AccountDeletion extends Event { + + private static final long serialVersionUID = 1L; + + /** + * @param value the ID of the contact that was deleted + * @since Envoy Common v0.3-beta + */ + public AccountDeletion(Long value) { + super(value); + } +} diff --git a/server/src/main/java/envoy/server/Startup.java b/server/src/main/java/envoy/server/Startup.java index e34cf91..413aa04 100755 --- a/server/src/main/java/envoy/server/Startup.java +++ b/server/src/main/java/envoy/server/Startup.java @@ -59,7 +59,8 @@ public final class Startup { new NameChangeProcessor(), new ProfilePicChangeProcessor(), new PasswordChangeRequestProcessor(), - new IssueProposalProcessor()))); + new IssueProposalProcessor(), + new AccountDeletionProcessor()))); // Initialize the current message ID final var persistenceManager = PersistenceManager.getInstance(); diff --git a/server/src/main/java/envoy/server/data/Message.java b/server/src/main/java/envoy/server/data/Message.java index 036c47a..72849b0 100755 --- a/server/src/main/java/envoy/server/data/Message.java +++ b/server/src/main/java/envoy/server/data/Message.java @@ -27,14 +27,18 @@ import envoy.data.Message.MessageStatus; @Entity @Table(name = "messages") @Inheritance(strategy = InheritanceType.SINGLE_TABLE) -@NamedQuery(name = Message.getPending, query = "SELECT m FROM Message m WHERE " - // Send to or by the user before last seen - + "(m.sender = :user OR m.recipient = :user) AND m.creationDate > :lastSeen " - // SENT to the user - + "OR m.recipient = :user AND m.status = envoy.data.Message$MessageStatus.SENT " - // Sent by the user and RECEIVED / READ after last seen - + "OR m.sender = :user AND (m.status = envoy.data.Message$MessageStatus.RECEIVED AND m.receivedDate > :lastSeen " - + "OR m.status = envoy.data.Message$MessageStatus.READ AND m.readDate > :lastSeen)") +@NamedQueries({ + @NamedQuery(name = Message.getPending, query = "SELECT m FROM Message m WHERE " + // Send to or by the user before last seen + + "(m.sender = :user OR m.recipient = :user) AND m.creationDate > :lastSeen " + // SENT to the user + + "OR m.recipient = :user AND m.status = envoy.data.Message$MessageStatus.SENT " + // Sent by the user and RECEIVED / READ after last seen + + "OR m.sender = :user AND (m.status = envoy.data.Message$MessageStatus.RECEIVED AND m.receivedDate > :lastSeen " + + "OR m.status = envoy.data.Message$MessageStatus.READ AND m.readDate > :lastSeen)"), + @NamedQuery(name = Message.deleteByRecipient, query = "DELETE FROM Message m WHERE m.recipient = :deleted OR m.sender = :deleted") + +}) public class Message { /** @@ -45,6 +49,13 @@ public class Message { */ public static final String getPending = "Message.getPending"; + /** + * Named query deleting all messages of a user (parameter {@code :deleted}). + * + * @since Envoy Server v0.3-beta + */ + public static final String deleteByRecipient = "Message.deleteByRecipient"; + @Id protected long id; diff --git a/server/src/main/java/envoy/server/data/PersistenceManager.java b/server/src/main/java/envoy/server/data/PersistenceManager.java index c79296b..ce61d17 100755 --- a/server/src/main/java/envoy/server/data/PersistenceManager.java +++ b/server/src/main/java/envoy/server/data/PersistenceManager.java @@ -123,6 +123,10 @@ public final class PersistenceManager { // Remove this contact from the contact list of his contacts for (final var remainingContact : contact.getContacts()) remainingContact.getContacts().remove(contact); + + entityManager + .createNamedQuery(Message.deleteByRecipient).setParameter("deleted", contact) + .executeUpdate(); }); remove(contact); } diff --git a/server/src/main/java/envoy/server/net/ConnectionManager.java b/server/src/main/java/envoy/server/net/ConnectionManager.java index 4a8ea2c..b2a046b 100755 --- a/server/src/main/java/envoy/server/net/ConnectionManager.java +++ b/server/src/main/java/envoy/server/net/ConnectionManager.java @@ -49,8 +49,10 @@ public final class ConnectionManager implements ISocketIdListener { // Notify contacts of this users offline-going final envoy.server.data.User user = PersistenceManager.getInstance().getUserByID(getUserIDBySocketID(socketID)); - user.setLastSeen(Instant.now()); - UserStatusChangeProcessor.updateUserStatus(user, UserStatus.OFFLINE); + if (user != null) { + user.setLastSeen(Instant.now()); + UserStatusChangeProcessor.updateUserStatus(user, UserStatus.OFFLINE); + } // Remove the socket sockets.entrySet().removeIf(e -> e.getValue() == socketID); diff --git a/server/src/main/java/envoy/server/processors/AccountDeletionProcessor.java b/server/src/main/java/envoy/server/processors/AccountDeletionProcessor.java new file mode 100644 index 0000000..5282fb8 --- /dev/null +++ b/server/src/main/java/envoy/server/processors/AccountDeletionProcessor.java @@ -0,0 +1,33 @@ +package envoy.server.processors; + +import java.io.IOException; +import java.time.Instant; + +import envoy.event.contact.AccountDeletion; + +import envoy.server.data.*; +import envoy.server.net.ObjectWriteProxy; + +/** + * @author Leon Hofmeister + * @since Envoy Server v0.3-beta + */ +public class AccountDeletionProcessor implements ObjectProcessor { + + private static final PersistenceManager persistenceManager = PersistenceManager.getInstance(); + + @Override + public void process(AccountDeletion input, long socketID, ObjectWriteProxy writeProxy) + throws IOException { + final var contact = persistenceManager.getContactByID(input.get()); + + contact.getContacts().forEach(c -> { + persistenceManager.removeContactBidirectional(contact, c); + if (c instanceof User) + ((User) c).setLatestContactDeletion(Instant.now()); + }); + + writeProxy.writeToOnlineContacts(contact.getContacts(), input); + persistenceManager.deleteContact(contact); + } +} diff --git a/server/src/main/java/envoy/server/processors/GroupResizeProcessor.java b/server/src/main/java/envoy/server/processors/GroupResizeProcessor.java index 9c42d3d..9efaf60 100644 --- a/server/src/main/java/envoy/server/processors/GroupResizeProcessor.java +++ b/server/src/main/java/envoy/server/processors/GroupResizeProcessor.java @@ -4,6 +4,7 @@ import java.time.Instant; import java.util.logging.Level; import envoy.event.GroupResize; +import envoy.event.contact.AccountDeletion; import envoy.util.EnvoyLog; import envoy.server.data.*; @@ -24,6 +25,12 @@ public final class GroupResizeProcessor implements ObjectProcessor final var group = persistenceManager.getGroupByID(groupResize.getGroupID()); final var sender = persistenceManager.getUserByID(groupResize.get().getID()); + // Inform the sender that this group has already been deleted + if (group == null) { + writeProxy.write(socketID, new AccountDeletion(groupResize.getGroupID())); + return; + } + // Perform the desired operation switch (groupResize.getOperation()) { case ADD: diff --git a/server/src/main/java/envoy/server/processors/UserOperationProcessor.java b/server/src/main/java/envoy/server/processors/UserOperationProcessor.java index d03d1d4..d108494 100755 --- a/server/src/main/java/envoy/server/processors/UserOperationProcessor.java +++ b/server/src/main/java/envoy/server/processors/UserOperationProcessor.java @@ -4,7 +4,7 @@ import java.time.Instant; import java.util.logging.*; import envoy.event.ElementOperation; -import envoy.event.contact.UserOperation; +import envoy.event.contact.*; import envoy.util.EnvoyLog; import envoy.server.data.PersistenceManager; @@ -22,10 +22,18 @@ public final class UserOperationProcessor implements ObjectProcessor Date: Sat, 24 Oct 2020 12:19:11 +0200 Subject: [PATCH 3/3] Remove account deletion on the server --- .../main/java/envoy/client/data/LocalDB.java | 14 ++++-- .../envoy/client/event}/AccountDeletion.java | 2 +- .../ui/controller/GroupCreationTab.java | 2 +- .../client/ui/settings/UserSettingsPane.java | 10 ++++- .../main/java/envoy/client/util/UserUtil.java | 43 ++++++++----------- client/src/main/resources/css/base.css | 11 +++++ client/src/main/resources/css/light.css | 4 ++ .../src/main/java/envoy/server/Startup.java | 3 +- .../main/java/envoy/server/data/Message.java | 27 ++++-------- .../envoy/server/data/PersistenceManager.java | 7 +-- .../processors/AccountDeletionProcessor.java | 33 -------------- .../GroupMessageStatusChangeProcessor.java | 2 + .../processors/GroupResizeProcessor.java | 7 +-- .../MessageStatusChangeProcessor.java | 2 + .../processors/UserOperationProcessor.java | 8 ++-- 15 files changed, 75 insertions(+), 100 deletions(-) rename {common/src/main/java/envoy/event/contact => client/src/main/java/envoy/client/event}/AccountDeletion.java (93%) delete mode 100644 server/src/main/java/envoy/server/processors/AccountDeletionProcessor.java diff --git a/client/src/main/java/envoy/client/data/LocalDB.java b/client/src/main/java/envoy/client/data/LocalDB.java index 547c9a0..da368ba 100644 --- a/client/src/main/java/envoy/client/data/LocalDB.java +++ b/client/src/main/java/envoy/client/data/LocalDB.java @@ -283,6 +283,15 @@ public final class LocalDB implements EventListener { * @since Envoy Client v0.3-beta */ public void delete() { + try { + + // Save ID generator - can be used for other users in that db + if (hasIDGenerator()) + SerializationUtils.write(idGeneratorFile, idGenerator); + } catch (final IOException e) { + EnvoyLog.getLogger(LocalDB.class).log(Level.SEVERE, "Unable to save local database: ", + e); + } if (lastLoginFile != null) lastLoginFile.delete(); userFile.delete(); @@ -427,10 +436,7 @@ public final class LocalDB implements EventListener { if (user.getID() == deletion.get()) logger.log(Level.WARNING, "I have been informed by the server that I have been deleted without even knowing it..."); - getChat(deletion.get()).ifPresent(chat -> { - chat.setDisabled(true); - chat.setUnderlyingContactDeleted(true); - }); + getChat(deletion.get()).ifPresent(chat -> chat.setDisabled(true)); } /** diff --git a/common/src/main/java/envoy/event/contact/AccountDeletion.java b/client/src/main/java/envoy/client/event/AccountDeletion.java similarity index 93% rename from common/src/main/java/envoy/event/contact/AccountDeletion.java rename to client/src/main/java/envoy/client/event/AccountDeletion.java index dabc0be..ef73462 100644 --- a/common/src/main/java/envoy/event/contact/AccountDeletion.java +++ b/client/src/main/java/envoy/client/event/AccountDeletion.java @@ -1,4 +1,4 @@ -package envoy.event.contact; +package envoy.client.event; import envoy.event.Event; diff --git a/client/src/main/java/envoy/client/ui/controller/GroupCreationTab.java b/client/src/main/java/envoy/client/ui/controller/GroupCreationTab.java index af5c054..f1f1b58 100644 --- a/client/src/main/java/envoy/client/ui/controller/GroupCreationTab.java +++ b/client/src/main/java/envoy/client/ui/controller/GroupCreationTab.java @@ -18,7 +18,7 @@ import envoy.event.contact.*; import envoy.util.Bounds; import envoy.client.data.*; -import envoy.client.event.BackEvent; +import envoy.client.event.*; import envoy.client.ui.control.*; import envoy.client.ui.listcell.ListCellFactory; diff --git a/client/src/main/java/envoy/client/ui/settings/UserSettingsPane.java b/client/src/main/java/envoy/client/ui/settings/UserSettingsPane.java index aa194c5..3a35061 100644 --- a/client/src/main/java/envoy/client/ui/settings/UserSettingsPane.java +++ b/client/src/main/java/envoy/client/ui/settings/UserSettingsPane.java @@ -13,6 +13,7 @@ import javafx.scene.image.*; import javafx.scene.input.InputEvent; import javafx.scene.layout.HBox; import javafx.stage.FileChooser; +import javafx.util.Duration; import dev.kske.eventbus.EventBus; @@ -38,7 +39,7 @@ public final class UserSettingsPane extends OnlineOnlySettingsPane { private final PasswordField newPasswordField = new PasswordField(); private final PasswordField repeatNewPasswordField = new PasswordField(); private final Button saveButton = new Button("Save"); - private final Button deleteAccountButton = new Button("Delete Account"); + private final Button deleteAccountButton = new Button("Delete Account (Locally)"); private static final EventBus eventBus = EventBus.getInstance(); private static final Logger logger = EnvoyLog.getLogger(UserSettingsPane.class); @@ -148,6 +149,13 @@ public final class UserSettingsPane extends OnlineOnlySettingsPane { // Displaying the delete account button deleteAccountButton.setAlignment(Pos.BASELINE_CENTER); deleteAccountButton.setOnAction(e -> UserUtil.deleteAccount()); + deleteAccountButton.setText("Delete Account (locally)"); + deleteAccountButton.setPrefHeight(25); + deleteAccountButton.getStyleClass().clear(); + deleteAccountButton.getStyleClass().add("danger-button"); + final var tooltip = new Tooltip("Remote deletion is currently unsupported."); + tooltip.setShowDelay(Duration.millis(100)); + deleteAccountButton.setTooltip(tooltip); getChildren().add(deleteAccountButton); } diff --git a/client/src/main/java/envoy/client/util/UserUtil.java b/client/src/main/java/envoy/client/util/UserUtil.java index aba9557..bc7d7fe 100644 --- a/client/src/main/java/envoy/client/util/UserUtil.java +++ b/client/src/main/java/envoy/client/util/UserUtil.java @@ -10,7 +10,7 @@ import dev.kske.eventbus.EventBus; import envoy.data.*; import envoy.data.User.UserStatus; import envoy.event.*; -import envoy.event.contact.*; +import envoy.event.contact.UserOperation; import envoy.util.EnvoyLog; import envoy.client.data.Context; @@ -129,32 +129,27 @@ public final class UserUtil { * @since Envoy Client v0.3-beta */ public static void deleteAccount() { - if (!context.getClient().isOnline()) - return; - else { - // Show the first wall of defense, if not disabled by the user - final var outerAlert = new Alert(AlertType.CONFIRMATION); - outerAlert.setContentText( - "Are you sure you want to delete your account entirely? This action can seriously not be undone."); - outerAlert.setTitle("Delete Account?"); - AlertHelper.confirmAction(outerAlert, () -> { + // Show the first wall of defense, if not disabled by the user + final var outerAlert = new Alert(AlertType.CONFIRMATION); + outerAlert.setContentText( + "Are you sure you want to delete your account entirely? This action can seriously not be undone."); + outerAlert.setTitle("Delete Account?"); + AlertHelper.confirmAction(outerAlert, () -> { - // Show the final wall of defense in every case - final var lastAlert = new Alert(AlertType.WARNING, - "Do you REALLY want to delete your account? Last Warning. Proceed?", - ButtonType.CANCEL, ButtonType.OK); - lastAlert.setTitle("Delete Account?"); - lastAlert.showAndWait().filter(ButtonType.OK::equals).ifPresent(b2 -> { + // Show the final wall of defense in every case + final var lastAlert = new Alert(AlertType.WARNING, + "Do you REALLY want to delete your account? Last Warning. Proceed?", + ButtonType.CANCEL, ButtonType.OK); + lastAlert.setTitle("Delete Account?"); + lastAlert.showAndWait().filter(ButtonType.OK::equals).ifPresent(b2 -> { - // Delete the account - context.getClient() - .send(new AccountDeletion(context.getLocalDB().getUser().getID())); - context.getLocalDB().delete(); - logger.log(Level.INFO, "The user just deleted his account. Goodbye."); - ShutdownHelper.exit(true); - }); + // Delete the account + // TODO: Notify server of account deletion + context.getLocalDB().delete(); + logger.log(Level.INFO, "The user just deleted his account. Goodbye."); + ShutdownHelper.exit(true); }); - } + }); } } diff --git a/client/src/main/resources/css/base.css b/client/src/main/resources/css/base.css index 4c46161..fdfc1b2 100644 --- a/client/src/main/resources/css/base.css +++ b/client/src/main/resources/css/base.css @@ -70,6 +70,17 @@ -fx-text-fill: gray; } +.danger-button { + -fx-background-color: red; + -fx-text-fill: white; + -fx-background-radius: 0.2em; +} + +.danger-button:hover { + -fx-scale-x: 1.05; + -fx-scale-y: 1.05; +} + .received-message { -fx-alignment: center-left; -fx-background-radius: 1.3em; diff --git a/client/src/main/resources/css/light.css b/client/src/main/resources/css/light.css index 7cd3ac1..81ac69b 100644 --- a/client/src/main/resources/css/light.css +++ b/client/src/main/resources/css/light.css @@ -30,6 +30,10 @@ -fx-background-color: black; } +.tooltip { + -fx-text-fill: black; +} + #login-input-field { -fx-border-color: black; } diff --git a/server/src/main/java/envoy/server/Startup.java b/server/src/main/java/envoy/server/Startup.java index 413aa04..e34cf91 100755 --- a/server/src/main/java/envoy/server/Startup.java +++ b/server/src/main/java/envoy/server/Startup.java @@ -59,8 +59,7 @@ public final class Startup { new NameChangeProcessor(), new ProfilePicChangeProcessor(), new PasswordChangeRequestProcessor(), - new IssueProposalProcessor(), - new AccountDeletionProcessor()))); + new IssueProposalProcessor()))); // Initialize the current message ID final var persistenceManager = PersistenceManager.getInstance(); diff --git a/server/src/main/java/envoy/server/data/Message.java b/server/src/main/java/envoy/server/data/Message.java index 72849b0..036c47a 100755 --- a/server/src/main/java/envoy/server/data/Message.java +++ b/server/src/main/java/envoy/server/data/Message.java @@ -27,18 +27,14 @@ import envoy.data.Message.MessageStatus; @Entity @Table(name = "messages") @Inheritance(strategy = InheritanceType.SINGLE_TABLE) -@NamedQueries({ - @NamedQuery(name = Message.getPending, query = "SELECT m FROM Message m WHERE " - // Send to or by the user before last seen - + "(m.sender = :user OR m.recipient = :user) AND m.creationDate > :lastSeen " - // SENT to the user - + "OR m.recipient = :user AND m.status = envoy.data.Message$MessageStatus.SENT " - // Sent by the user and RECEIVED / READ after last seen - + "OR m.sender = :user AND (m.status = envoy.data.Message$MessageStatus.RECEIVED AND m.receivedDate > :lastSeen " - + "OR m.status = envoy.data.Message$MessageStatus.READ AND m.readDate > :lastSeen)"), - @NamedQuery(name = Message.deleteByRecipient, query = "DELETE FROM Message m WHERE m.recipient = :deleted OR m.sender = :deleted") - -}) +@NamedQuery(name = Message.getPending, query = "SELECT m FROM Message m WHERE " + // Send to or by the user before last seen + + "(m.sender = :user OR m.recipient = :user) AND m.creationDate > :lastSeen " + // SENT to the user + + "OR m.recipient = :user AND m.status = envoy.data.Message$MessageStatus.SENT " + // Sent by the user and RECEIVED / READ after last seen + + "OR m.sender = :user AND (m.status = envoy.data.Message$MessageStatus.RECEIVED AND m.receivedDate > :lastSeen " + + "OR m.status = envoy.data.Message$MessageStatus.READ AND m.readDate > :lastSeen)") public class Message { /** @@ -49,13 +45,6 @@ public class Message { */ public static final String getPending = "Message.getPending"; - /** - * Named query deleting all messages of a user (parameter {@code :deleted}). - * - * @since Envoy Server v0.3-beta - */ - public static final String deleteByRecipient = "Message.deleteByRecipient"; - @Id protected long id; diff --git a/server/src/main/java/envoy/server/data/PersistenceManager.java b/server/src/main/java/envoy/server/data/PersistenceManager.java index ce61d17..23f19d9 100755 --- a/server/src/main/java/envoy/server/data/PersistenceManager.java +++ b/server/src/main/java/envoy/server/data/PersistenceManager.java @@ -121,12 +121,9 @@ public final class PersistenceManager { transaction(() -> { // Remove this contact from the contact list of his contacts - for (final var remainingContact : contact.getContacts()) + for (final var remainingContact : contact.contacts) remainingContact.getContacts().remove(contact); - - entityManager - .createNamedQuery(Message.deleteByRecipient).setParameter("deleted", contact) - .executeUpdate(); + contact.contacts.clear(); }); remove(contact); } diff --git a/server/src/main/java/envoy/server/processors/AccountDeletionProcessor.java b/server/src/main/java/envoy/server/processors/AccountDeletionProcessor.java deleted file mode 100644 index 5282fb8..0000000 --- a/server/src/main/java/envoy/server/processors/AccountDeletionProcessor.java +++ /dev/null @@ -1,33 +0,0 @@ -package envoy.server.processors; - -import java.io.IOException; -import java.time.Instant; - -import envoy.event.contact.AccountDeletion; - -import envoy.server.data.*; -import envoy.server.net.ObjectWriteProxy; - -/** - * @author Leon Hofmeister - * @since Envoy Server v0.3-beta - */ -public class AccountDeletionProcessor implements ObjectProcessor { - - private static final PersistenceManager persistenceManager = PersistenceManager.getInstance(); - - @Override - public void process(AccountDeletion input, long socketID, ObjectWriteProxy writeProxy) - throws IOException { - final var contact = persistenceManager.getContactByID(input.get()); - - contact.getContacts().forEach(c -> { - persistenceManager.removeContactBidirectional(contact, c); - if (c instanceof User) - ((User) c).setLatestContactDeletion(Instant.now()); - }); - - writeProxy.writeToOnlineContacts(contact.getContacts(), input); - persistenceManager.deleteContact(contact); - } -} diff --git a/server/src/main/java/envoy/server/processors/GroupMessageStatusChangeProcessor.java b/server/src/main/java/envoy/server/processors/GroupMessageStatusChangeProcessor.java index fffe41b..b9482f1 100644 --- a/server/src/main/java/envoy/server/processors/GroupMessageStatusChangeProcessor.java +++ b/server/src/main/java/envoy/server/processors/GroupMessageStatusChangeProcessor.java @@ -38,6 +38,8 @@ public final class GroupMessageStatusChangeProcessor } GroupMessage gmsg = (GroupMessage) persistenceManager.getMessageByID(statusChange.getID()); + if (gmsg == null) + return; // Any other status than READ is not supposed to be sent to the server if (statusChange.get() != MessageStatus.READ) { diff --git a/server/src/main/java/envoy/server/processors/GroupResizeProcessor.java b/server/src/main/java/envoy/server/processors/GroupResizeProcessor.java index 9efaf60..92b3f84 100644 --- a/server/src/main/java/envoy/server/processors/GroupResizeProcessor.java +++ b/server/src/main/java/envoy/server/processors/GroupResizeProcessor.java @@ -4,7 +4,6 @@ import java.time.Instant; import java.util.logging.Level; import envoy.event.GroupResize; -import envoy.event.contact.AccountDeletion; import envoy.util.EnvoyLog; import envoy.server.data.*; @@ -25,11 +24,9 @@ public final class GroupResizeProcessor implements ObjectProcessor final var group = persistenceManager.getGroupByID(groupResize.getGroupID()); final var sender = persistenceManager.getUserByID(groupResize.get().getID()); - // Inform the sender that this group has already been deleted - if (group == null) { - writeProxy.write(socketID, new AccountDeletion(groupResize.getGroupID())); + // TODO: Inform the sender that this group has already been deleted + if (group == null) return; - } // Perform the desired operation switch (groupResize.getOperation()) { diff --git a/server/src/main/java/envoy/server/processors/MessageStatusChangeProcessor.java b/server/src/main/java/envoy/server/processors/MessageStatusChangeProcessor.java index d954af5..efc407b 100755 --- a/server/src/main/java/envoy/server/processors/MessageStatusChangeProcessor.java +++ b/server/src/main/java/envoy/server/processors/MessageStatusChangeProcessor.java @@ -32,6 +32,8 @@ public final class MessageStatusChangeProcessor implements ObjectProcessor