Add Local Account Deletion #108
@@ -22,20 +22,21 @@ import envoy.client.net.WriteProxy;
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
public class Chat implements Serializable {
 | 
					public class Chat implements Serializable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected transient ObservableList<Message> messages = FXCollections.observableArrayList();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected final Contact recipient;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected boolean	disabled;
 | 
						protected boolean	disabled;
 | 
				
			||||||
 | 
						protected boolean	underlyingContactDeleted;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Stores the last time an {@link envoy.event.IsTyping} event has been sent.
 | 
						 * Stores the last time an {@link envoy.event.IsTyping} event has been sent.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	protected transient long lastWritingEvent;
 | 
						protected transient long lastWritingEvent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected transient ObservableList<Message> messages = FXCollections.observableArrayList();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	protected int						unreadAmount;
 | 
						protected int						unreadAmount;
 | 
				
			||||||
	protected static IntegerProperty	totalUnreadAmount	= new SimpleIntegerProperty();
 | 
						protected static IntegerProperty	totalUnreadAmount	= new SimpleIntegerProperty();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected final Contact recipient;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	private static final long serialVersionUID = 2L;
 | 
						private static final long serialVersionUID = 2L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -248,6 +248,10 @@ public final class LocalDB implements EventListener {
 | 
				
			|||||||
	 */
 | 
						 */
 | 
				
			||||||
	@Event(eventType = EnvoyCloseEvent.class, priority = 500)
 | 
						@Event(eventType = EnvoyCloseEvent.class, priority = 500)
 | 
				
			||||||
	private synchronized void save() {
 | 
						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...");
 | 
							EnvoyLog.getLogger(LocalDB.class).log(Level.FINER, "Saving local database...");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Save users
 | 
							// Save users
 | 
				
			||||||
@@ -273,6 +277,29 @@ public final class LocalDB implements EventListener {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Deletes any local remnant of this user.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @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();
 | 
				
			||||||
 | 
							users.remove(user.getName());
 | 
				
			||||||
 | 
							userFile = null;
 | 
				
			||||||
 | 
							onLogout();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Event(priority = 500)
 | 
						@Event(priority = 500)
 | 
				
			||||||
	private void onMessage(Message msg) {
 | 
						private void onMessage(Message msg) {
 | 
				
			||||||
		if (msg.getStatus() == MessageStatus.SENT)
 | 
							if (msg.getStatus() == MessageStatus.SENT)
 | 
				
			||||||
@@ -404,6 +431,14 @@ public final class LocalDB implements EventListener {
 | 
				
			|||||||
		getChat(event.get().getID()).ifPresent(chat -> chat.setDisabled(true));
 | 
							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));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * @return a {@code Map<String, User>} of all users stored locally with their user names as keys
 | 
						 * @return a {@code Map<String, User>} of all users stored locally with their user names as keys
 | 
				
			||||||
	 * @since Envoy Client v0.2-alpha
 | 
						 * @since Envoy Client v0.2-alpha
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										22
									
								
								client/src/main/java/envoy/client/event/AccountDeletion.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								client/src/main/java/envoy/client/event/AccountDeletion.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					package envoy.client.event;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import envoy.event.Event;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Signifies the deletion of an account.
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * @author Leon Hofmeister
 | 
				
			||||||
 | 
					 * @since Envoy Common v0.3-beta
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class AccountDeletion extends Event<Long> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -6,6 +6,7 @@ import java.io.*;
 | 
				
			|||||||
import java.nio.file.Files;
 | 
					import java.nio.file.Files;
 | 
				
			||||||
import java.time.LocalDateTime;
 | 
					import java.time.LocalDateTime;
 | 
				
			||||||
import java.time.format.DateTimeFormatter;
 | 
					import java.time.format.DateTimeFormatter;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
import java.util.logging.*;
 | 
					import java.util.logging.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import javafx.animation.RotateTransition;
 | 
					import javafx.animation.RotateTransition;
 | 
				
			||||||
@@ -31,12 +32,13 @@ import envoy.data.*;
 | 
				
			|||||||
import envoy.data.Attachment.AttachmentType;
 | 
					import envoy.data.Attachment.AttachmentType;
 | 
				
			||||||
import envoy.data.Message.MessageStatus;
 | 
					import envoy.data.Message.MessageStatus;
 | 
				
			||||||
import envoy.event.*;
 | 
					import envoy.event.*;
 | 
				
			||||||
import envoy.event.contact.UserOperation;
 | 
					import envoy.event.contact.*;
 | 
				
			||||||
import envoy.exception.EnvoyException;
 | 
					import envoy.exception.EnvoyException;
 | 
				
			||||||
import envoy.util.EnvoyLog;
 | 
					import envoy.util.EnvoyLog;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import envoy.client.data.*;
 | 
					import envoy.client.data.*;
 | 
				
			||||||
import envoy.client.data.audio.AudioRecorder;
 | 
					import envoy.client.data.audio.AudioRecorder;
 | 
				
			||||||
 | 
					import envoy.client.data.shortcuts.KeyboardMapping;
 | 
				
			||||||
import envoy.client.event.*;
 | 
					import envoy.client.event.*;
 | 
				
			||||||
import envoy.client.net.*;
 | 
					import envoy.client.net.*;
 | 
				
			||||||
import envoy.client.ui.*;
 | 
					import envoy.client.ui.*;
 | 
				
			||||||
@@ -51,7 +53,7 @@ import envoy.client.util.*;
 | 
				
			|||||||
 * @author Kai S. K. Engelbart
 | 
					 * @author Kai S. K. Engelbart
 | 
				
			||||||
 * @since Envoy Client v0.1-beta
 | 
					 * @since Envoy Client v0.1-beta
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public final class ChatScene implements EventListener, Restorable {
 | 
					public final class ChatScene implements EventListener, Restorable, KeyboardMapping {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@FXML
 | 
						@FXML
 | 
				
			||||||
	private ListView<Message> messageList;
 | 
						private ListView<Message> messageList;
 | 
				
			||||||
@@ -346,6 +348,11 @@ public final class ChatScene implements EventListener, Restorable {
 | 
				
			|||||||
		eventBus.removeListener(this);
 | 
							eventBus.removeListener(this);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Event(eventType = AccountDeletion.class)
 | 
				
			||||||
 | 
						private void onAccountDeletion() {
 | 
				
			||||||
 | 
							Platform.runLater(chatList::refresh);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public void onRestore() {
 | 
						public void onRestore() {
 | 
				
			||||||
		updateRemainingCharsLabel();
 | 
							updateRemainingCharsLabel();
 | 
				
			||||||
@@ -871,4 +878,25 @@ public final class ChatScene implements EventListener, Restorable {
 | 
				
			|||||||
			: c -> c.getRecipient().getName().toLowerCase()
 | 
								: c -> c.getRecipient().getName().toLowerCase()
 | 
				
			||||||
				.contains(contactSearch.getText().toLowerCase()));
 | 
									.contains(contactSearch.getText().toLowerCase()));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public Map<KeyCombination, Runnable> getKeyboardShortcuts() {
 | 
				
			||||||
 | 
							return Map.<KeyCombination, Runnable>of(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// 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
 | 
				
			||||||
 | 
								}, new KeyCodeCombination(KeyCode.K, KeyCombination.CONTROL_DOWN), () -> {
 | 
				
			||||||
 | 
									messageTextArea
 | 
				
			||||||
 | 
										.setText(
 | 
				
			||||||
 | 
											messageTextArea.getText().substring(0, messageTextArea.getCaretPosition()));
 | 
				
			||||||
 | 
									checkPostConditions(false);
 | 
				
			||||||
 | 
									messageTextArea.positionCaret(messageTextArea.getText().length());
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,11 +14,11 @@ import dev.kske.eventbus.*;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import envoy.data.*;
 | 
					import envoy.data.*;
 | 
				
			||||||
import envoy.event.GroupCreation;
 | 
					import envoy.event.GroupCreation;
 | 
				
			||||||
import envoy.event.contact.UserOperation;
 | 
					import envoy.event.contact.*;
 | 
				
			||||||
import envoy.util.Bounds;
 | 
					import envoy.util.Bounds;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import envoy.client.data.*;
 | 
					import envoy.client.data.*;
 | 
				
			||||||
import envoy.client.event.BackEvent;
 | 
					import envoy.client.event.*;
 | 
				
			||||||
import envoy.client.ui.control.*;
 | 
					import envoy.client.ui.control.*;
 | 
				
			||||||
import envoy.client.ui.listcell.ListCellFactory;
 | 
					import envoy.client.ui.listcell.ListCellFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -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)));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,7 @@ import javafx.scene.image.*;
 | 
				
			|||||||
import javafx.scene.input.InputEvent;
 | 
					import javafx.scene.input.InputEvent;
 | 
				
			||||||
import javafx.scene.layout.HBox;
 | 
					import javafx.scene.layout.HBox;
 | 
				
			||||||
import javafx.stage.FileChooser;
 | 
					import javafx.stage.FileChooser;
 | 
				
			||||||
 | 
					import javafx.util.Duration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import dev.kske.eventbus.EventBus;
 | 
					import dev.kske.eventbus.EventBus;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -20,7 +21,7 @@ import envoy.event.*;
 | 
				
			|||||||
import envoy.util.*;
 | 
					import envoy.util.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import envoy.client.ui.control.ProfilePicImageView;
 | 
					import envoy.client.ui.control.ProfilePicImageView;
 | 
				
			||||||
import envoy.client.util.IconUtil;
 | 
					import envoy.client.util.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @author Leon Hofmeister
 | 
					 * @author Leon Hofmeister
 | 
				
			||||||
@@ -38,6 +39,7 @@ public final class UserSettingsPane extends OnlineOnlySettingsPane {
 | 
				
			|||||||
	private final PasswordField	newPasswordField		= new PasswordField();
 | 
						private final PasswordField	newPasswordField		= new PasswordField();
 | 
				
			||||||
	private final PasswordField	repeatNewPasswordField	= new PasswordField();
 | 
						private final PasswordField	repeatNewPasswordField	= new PasswordField();
 | 
				
			||||||
	private final Button		saveButton				= new Button("Save");
 | 
						private final Button		saveButton				= new Button("Save");
 | 
				
			||||||
 | 
						private final Button		deleteAccountButton		= new Button("Delete Account (Locally)");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private static final EventBus	eventBus	= EventBus.getInstance();
 | 
						private static final EventBus	eventBus	= EventBus.getInstance();
 | 
				
			||||||
	private static final Logger		logger		= EnvoyLog.getLogger(UserSettingsPane.class);
 | 
						private static final Logger		logger		= EnvoyLog.getLogger(UserSettingsPane.class);
 | 
				
			||||||
@@ -112,15 +114,18 @@ public final class UserSettingsPane extends OnlineOnlySettingsPane {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		final PasswordField[]					passwordFields	=
 | 
							final PasswordField[]					passwordFields	=
 | 
				
			||||||
			{ currentPasswordField, newPasswordField, repeatNewPasswordField };
 | 
								{ currentPasswordField, newPasswordField, repeatNewPasswordField };
 | 
				
			||||||
		final EventHandler<? super InputEvent>	passwordEntered	= e -> {
 | 
							final EventHandler<? super InputEvent>	passwordEntered	=
 | 
				
			||||||
 | 
								e -> {
 | 
				
			||||||
																		newPassword	=
 | 
																							newPassword	=
 | 
				
			||||||
																		newPasswordField.getText();
 | 
																								newPasswordField
 | 
				
			||||||
																	validPassword	= newPassword
 | 
																									.getText();
 | 
				
			||||||
																		.equals(
 | 
																							validPassword =
 | 
				
			||||||
 | 
																								newPassword.equals(
 | 
				
			||||||
																				repeatNewPasswordField
 | 
																									repeatNewPasswordField
 | 
				
			||||||
																					.getText())
 | 
																										.getText())
 | 
				
			||||||
																				&& !newPasswordField
 | 
																									&& !newPasswordField
 | 
				
			||||||
																			.getText().isBlank();
 | 
																										.getText()
 | 
				
			||||||
 | 
																										.isBlank();
 | 
				
			||||||
																	};
 | 
																						};
 | 
				
			||||||
		newPasswordField.setOnInputMethodTextChanged(passwordEntered);
 | 
							newPasswordField.setOnInputMethodTextChanged(passwordEntered);
 | 
				
			||||||
		newPasswordField.setOnKeyTyped(passwordEntered);
 | 
							newPasswordField.setOnKeyTyped(passwordEntered);
 | 
				
			||||||
@@ -140,6 +145,18 @@ public final class UserSettingsPane extends OnlineOnlySettingsPane {
 | 
				
			|||||||
			.setOnAction(e -> save(currentPasswordField.getText()));
 | 
								.setOnAction(e -> save(currentPasswordField.getText()));
 | 
				
			||||||
		saveButton.setAlignment(Pos.BOTTOM_RIGHT);
 | 
							saveButton.setAlignment(Pos.BOTTOM_RIGHT);
 | 
				
			||||||
		getChildren().add(saveButton);
 | 
							getChildren().add(saveButton);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 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);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@ package envoy.client.util;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import java.util.logging.*;
 | 
					import java.util.logging.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import javafx.scene.control.Alert;
 | 
					import javafx.scene.control.*;
 | 
				
			||||||
import javafx.scene.control.Alert.AlertType;
 | 
					import javafx.scene.control.Alert.AlertType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import dev.kske.eventbus.EventBus;
 | 
					import dev.kske.eventbus.EventBus;
 | 
				
			||||||
@@ -121,4 +121,35 @@ 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() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 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
 | 
				
			||||||
 | 
									// TODO: Notify server of account deletion
 | 
				
			||||||
 | 
									context.getLocalDB().delete();
 | 
				
			||||||
 | 
									logger.log(Level.INFO, "The user just deleted his account. Goodbye.");
 | 
				
			||||||
 | 
									ShutdownHelper.exit(true);
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -70,6 +70,17 @@
 | 
				
			|||||||
	-fx-text-fill: gray;
 | 
						-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 {
 | 
					.received-message {
 | 
				
			||||||
	-fx-alignment: center-left;
 | 
						-fx-alignment: center-left;
 | 
				
			||||||
	-fx-background-radius: 1.3em;
 | 
						-fx-background-radius: 1.3em;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,6 +30,10 @@
 | 
				
			|||||||
	-fx-background-color: black;
 | 
						-fx-background-color: black;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.tooltip {
 | 
				
			||||||
 | 
						-fx-text-fill: black;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#login-input-field {
 | 
					#login-input-field {
 | 
				
			||||||
	-fx-border-color: black;
 | 
						-fx-border-color: black;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -121,8 +121,9 @@ public final class PersistenceManager {
 | 
				
			|||||||
		transaction(() -> {
 | 
							transaction(() -> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Remove this contact from the contact list of his contacts
 | 
								// 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);
 | 
									remainingContact.getContacts().remove(contact);
 | 
				
			||||||
 | 
								contact.contacts.clear();
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
		remove(contact);
 | 
							remove(contact);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -49,8 +49,10 @@ public final class ConnectionManager implements ISocketIdListener {
 | 
				
			|||||||
			// Notify contacts of this users offline-going
 | 
								// Notify contacts of this users offline-going
 | 
				
			||||||
			final envoy.server.data.User user =
 | 
								final envoy.server.data.User user =
 | 
				
			||||||
				PersistenceManager.getInstance().getUserByID(getUserIDBySocketID(socketID));
 | 
									PersistenceManager.getInstance().getUserByID(getUserIDBySocketID(socketID));
 | 
				
			||||||
 | 
								if (user != null) {
 | 
				
			||||||
				user.setLastSeen(Instant.now());
 | 
									user.setLastSeen(Instant.now());
 | 
				
			||||||
				UserStatusChangeProcessor.updateUserStatus(user, UserStatus.OFFLINE);
 | 
									UserStatusChangeProcessor.updateUserStatus(user, UserStatus.OFFLINE);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Remove the socket
 | 
								// Remove the socket
 | 
				
			||||||
			sockets.entrySet().removeIf(e -> e.getValue() == socketID);
 | 
								sockets.entrySet().removeIf(e -> e.getValue() == socketID);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,6 +38,8 @@ public final class GroupMessageStatusChangeProcessor
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		GroupMessage gmsg = (GroupMessage) persistenceManager.getMessageByID(statusChange.getID());
 | 
							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
 | 
							// Any other status than READ is not supposed to be sent to the server
 | 
				
			||||||
		if (statusChange.get() != MessageStatus.READ) {
 | 
							if (statusChange.get() != MessageStatus.READ) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,6 +24,10 @@ public final class GroupResizeProcessor implements ObjectProcessor<GroupResize>
 | 
				
			|||||||
		final var	group	= persistenceManager.getGroupByID(groupResize.getGroupID());
 | 
							final var	group	= persistenceManager.getGroupByID(groupResize.getGroupID());
 | 
				
			||||||
		final var	sender	= persistenceManager.getUserByID(groupResize.get().getID());
 | 
							final var	sender	= persistenceManager.getUserByID(groupResize.get().getID());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// TODO: Inform the sender that this group has already been deleted
 | 
				
			||||||
 | 
							if (group == null)
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Perform the desired operation
 | 
							// Perform the desired operation
 | 
				
			||||||
		switch (groupResize.getOperation()) {
 | 
							switch (groupResize.getOperation()) {
 | 
				
			||||||
			case ADD:
 | 
								case ADD:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,6 +32,8 @@ public final class MessageStatusChangeProcessor implements ObjectProcessor<Messa
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		final var msg = persistenceManager.getMessageByID(statusChange.getID());
 | 
							final var msg = persistenceManager.getMessageByID(statusChange.getID());
 | 
				
			||||||
 | 
							if (msg == null)
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
		msg.read();
 | 
							msg.read();
 | 
				
			||||||
		persistenceManager.updateMessage(msg);
 | 
							persistenceManager.updateMessage(msg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,9 +22,15 @@ public final class UserOperationProcessor implements ObjectProcessor<UserOperati
 | 
				
			|||||||
	private static final PersistenceManager	persistenceManager	= PersistenceManager.getInstance();
 | 
						private static final PersistenceManager	persistenceManager	= PersistenceManager.getInstance();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public void process(UserOperation evt, long socketId, ObjectWriteProxy writeProxy) {
 | 
						public void process(UserOperation evt, long socketID, ObjectWriteProxy writeProxy) {
 | 
				
			||||||
		final long	userID		= ConnectionManager.getInstance().getUserIDBySocketID(socketId);
 | 
							final long	userID		= ConnectionManager.getInstance().getUserIDBySocketID(socketID);
 | 
				
			||||||
		final long	contactID	= evt.get().getID();
 | 
							final long	contactID	= evt.get().getID();
 | 
				
			||||||
 | 
							final var	recipient	= persistenceManager.getUserByID(contactID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// TODO: Inform the sender if the requested contact has already been deleted
 | 
				
			||||||
 | 
							if (recipient == null)
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		final var sender = persistenceManager.getUserByID(userID);
 | 
							final var sender = persistenceManager.getUserByID(userID);
 | 
				
			||||||
		switch (evt.getOperationType()) {
 | 
							switch (evt.getOperationType()) {
 | 
				
			||||||
			case ADD:
 | 
								case ADD:
 | 
				
			||||||
@@ -45,7 +51,7 @@ public final class UserOperationProcessor implements ObjectProcessor<UserOperati
 | 
				
			|||||||
				sender.setLatestContactDeletion(Instant.now());
 | 
									sender.setLatestContactDeletion(Instant.now());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				// 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());
 | 
									recipient.setLatestContactDeletion(Instant.now());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				// Notify the removed contact if online
 | 
									// Notify the removed contact if online
 | 
				
			||||||
				if (connectionManager.isOnline(contactID))
 | 
									if (connectionManager.isOnline(contactID))
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user