Contact Deletion #97
| @@ -57,7 +57,9 @@ public class Chat implements Serializable { | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public String toString() { return String.format("%s[recipient=%s,messages=%d]", getClass().getSimpleName(), recipient, messages.size()); } | ||||
| 	public String toString() { | ||||
| 		return String.format("%s[recipient=%s,messages=%d, disabled=%b]", getClass().getSimpleName(), recipient, messages.size(), disabled); | ||||
| 
					
					delvh marked this conversation as resolved
					
						
						
							Outdated
						
					
				 | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Generates a hash code based on the recipient. | ||||
|   | ||||
| @@ -1,11 +1,14 @@ | ||||
| package envoy.client.data; | ||||
|  | ||||
| import static java.util.function.Predicate.not; | ||||
|  | ||||
| import java.io.*; | ||||
| import java.nio.channels.*; | ||||
| import java.nio.file.StandardOpenOption; | ||||
| import java.time.Instant; | ||||
| import java.util.*; | ||||
| import java.util.logging.*; | ||||
| import java.util.stream.Stream; | ||||
|  | ||||
| import javafx.application.Platform; | ||||
| import javafx.collections.*; | ||||
| @@ -35,12 +38,13 @@ public final class LocalDB implements EventListener { | ||||
|  | ||||
| 	// Data | ||||
| 	private User					user; | ||||
| 	private Map<String, User>		users				= Collections.synchronizedMap(new HashMap<>()); | ||||
| 	private ObservableList<Chat>	chats				= FXCollections.observableArrayList(); | ||||
| 	private Map<String, User>		users		= Collections.synchronizedMap(new HashMap<>()); | ||||
| 	private ObservableList<Chat>	chats		= FXCollections.observableArrayList(); | ||||
| 	private IDGenerator				idGenerator; | ||||
| 	private CacheMap				cacheMap			= new CacheMap(); | ||||
| 	private CacheMap				cacheMap	= new CacheMap(); | ||||
| 	private String					authToken; | ||||
| 	private Set<? extends Contact>	originalContacts	= Set.of(); | ||||
| 	private boolean					contactsChanged; | ||||
| 	private Stream<Chat>			changedChats; | ||||
| 
					
					delvh marked this conversation as resolved
					
						
						
							Outdated
						
					
				 
				
					
						kske
						commented  Integrate this into  Integrate this into `loadUserData` as discussed. | ||||
|  | ||||
| 	// Auto save timer | ||||
| 	private Timer	autoSaver; | ||||
| @@ -138,7 +142,19 @@ public final class LocalDB implements EventListener { | ||||
| 		if (user == null) throw new IllegalStateException("Client user is null, cannot initialize user storage"); | ||||
| 		userFile = new File(dbDir, user.getID() + ".db"); | ||||
| 		try (var in = new ObjectInputStream(new FileInputStream(userFile))) { | ||||
| 			chats		= FXCollections.observableList((List<Chat>) in.readObject()); | ||||
| 			chats = FXCollections.observableList((List<Chat>) in.readObject()); | ||||
|  | ||||
| 			// Some chats have changed and should not be overridden by the saved values | ||||
| 
					
					delvh marked this conversation as resolved
					
						
						
							Outdated
						
					
				 
				
					
						kske
						commented  I think you mean 'overwritten'. I think you mean 'overwritten'. 
				
					
						delvh
						commented  See here. See [here](https://www.dict.cc/?s=%C3%BCberschrieben). 
				
					
						delvh
						commented  Past tense of override. Past tense of override. 
				
					
						kske
						commented  I know what the past tense of override is. What you want to prevent here is the destruction of old data by the recording new data over it, which is also known as overwriting. Look here for a comparison of both terms. I know what the past tense of override is.
What you want to prevent here is the destruction of old data by the recording new data over it, which is also known as overwriting.
Look [here](https://wikidiff.com/overwrite/override) for a comparison of both terms. 
				
					
						delvh
						commented  Still, both are applicable here. My preference is  Still, both are applicable here. My preference is `overridden`, yours is `overwritten`.
Let's give @DieGurke the vote on what stays in. (How great that we have an odd amount of members, I don't want to imagine disagreements with an even amount of members...) | ||||
| 			if (changedChats != null) { | ||||
| 				changedChats.forEach(chat -> { | ||||
| 					final var chatIndex = chats.indexOf(chat); | ||||
| 					if (chatIndex == -1) return; | ||||
| 
					
					kske marked this conversation as resolved
					
						
						
							Outdated
						
					
				 
				
					
						kske
						commented  Can this even happen? If it can, should it be logged? Can this even happen? If it can, should it be logged? 
				
					
						delvh
						commented  While it could theoretically occur, even if the user deleted a group while being offline, it should not happen. While it could theoretically occur, even if the user deleted a group while being offline, it should not happen.
Removing it now. | ||||
| 					else chats.set(chatIndex, chat); | ||||
| 				}); | ||||
|  | ||||
| 				// loadUserData can get called two (or more?) times during application lifecycle | ||||
| 				changedChats = null; | ||||
| 			} | ||||
| 			cacheMap	= (CacheMap) in.readObject(); | ||||
| 			lastSync	= (Instant) in.readObject(); | ||||
| 		} finally { | ||||
| @@ -324,7 +340,7 @@ public final class LocalDB implements EventListener { | ||||
| 	private void onOwnStatusChange(OwnStatusChange statusChange) { user.setStatus(statusChange.get()); } | ||||
|  | ||||
| 	@Event(eventType = ContactsChangedSinceLastLogin.class, priority = 500) | ||||
| 	private void onContactsChangedSinceLastLogin() { if (user != null) originalContacts = user.getContacts(); } | ||||
| 	private void onContactsChangedSinceLastLogin() { if (user != null) contactsChanged = true; } | ||||
| 
					
					delvh marked this conversation as resolved
					
						
						
							Outdated
						
					
				 
				
					
						kske
						commented  Please document why this is necessary, or at least give some conclusive message to the user. Please document why this is necessary, or at least give some conclusive message to the user. | ||||
|  | ||||
| 	/** | ||||
| 	 * @return a {@code Map<String, User>} of all users stored locally with their | ||||
| @@ -363,13 +379,12 @@ public final class LocalDB implements EventListener { | ||||
| 	 */ | ||||
| 	public void setUserAndMergeContacts(User user) { | ||||
| 		this.user = user; | ||||
| 		if (originalContacts.isEmpty()) return; | ||||
| 		else { | ||||
| 		if (contactsChanged) | ||||
|  | ||||
| 			// mark all chats of deleted contacts | ||||
| 			originalContacts.removeAll(user.getContacts()); | ||||
| 			originalContacts.forEach(contact -> getChat(contact.getID()).ifPresent(chat -> chat.setDisabled(true))); | ||||
| 		} | ||||
| 			// Mark chats as disabled if a contact is no longer in this users contact list | ||||
| 			changedChats = chats.stream() | ||||
| 				.filter(not(chat -> user.getContacts().contains(chat.getRecipient()))) | ||||
| 				.peek(chat -> { chat.setDisabled(true); logger.log(Level.INFO, String.format("Marked %s as blocked.", chat.getRecipient())); }); | ||||
| 	} | ||||
| 
					
					delvh marked this conversation as resolved
					
						
						
							Outdated
						
					
				 
				
					
						kske
						commented  Maybe log 'Disabled chat ...' here instead. Maybe log 'Disabled chat ...' here instead. | ||||
|  | ||||
| 	/** | ||||
|   | ||||
| @@ -272,8 +272,7 @@ public final class ChatScene implements EventListener, Restorable { | ||||
| 	@Event | ||||
| 	private void onUserOperation(UserOperation operation) { | ||||
| 		// All ADD dependant logic resides in LocalDB | ||||
| 		if (operation.getOperationType().equals(ElementOperation.REMOVE)) | ||||
| 			if (currentChat != null && currentChat.getRecipient().equals(operation.get())) Platform.runLater(this::resetState); | ||||
| 		if (operation.getOperationType().equals(ElementOperation.REMOVE)) Platform.runLater(() -> disableChat(operation.get(), true)); | ||||
| 	} | ||||
|  | ||||
| 
					
					delvh marked this conversation as resolved
					
				 
				
					
						kske
						commented  Why not use  Why not use `Optional#ifPresent` here? | ||||
| 	@Event | ||||
|   | ||||
| @@ -124,5 +124,8 @@ public class ContactSearchTab implements EventListener { | ||||
| 	} | ||||
|  | ||||
| 	@FXML | ||||
| 	private void backButtonClicked() { eventBus.dispatch(new BackEvent()); } | ||||
| 	private void backButtonClicked() { | ||||
| 		searchBar.setText(""); | ||||
| 		eventBus.dispatch(new BackEvent()); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -82,7 +82,7 @@ public class GroupCreationTab implements EventListener { | ||||
| 				.map(User.class::cast) | ||||
| 				.collect(Collectors.toList())); | ||||
| 		resizeQuickSelectSpace(0); | ||||
| 		quickSelectList.addEventFilter(MouseEvent.MOUSE_PRESSED, evt -> evt.consume()); | ||||
| 		quickSelectList.addEventFilter(MouseEvent.MOUSE_PRESSED, MouseEvent::consume); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| @@ -169,7 +169,7 @@ public class GroupCreationTab implements EventListener { | ||||
|  | ||||
| 	/** | ||||
| 	 * Removes an element from the quickSelectList. | ||||
| 	 *  | ||||
| 	 * | ||||
| 	 * @param element the element to be removed. | ||||
| 	 * @since Envoy Client v0.3-beta | ||||
| 	 */ | ||||
| @@ -238,7 +238,7 @@ public class GroupCreationTab implements EventListener { | ||||
| 		Platform.runLater(() -> { | ||||
| 			switch (operation.getOperationType()) { | ||||
| 				case ADD: | ||||
| 					userList.getItems().add((User) operation.get()); | ||||
| 					userList.getItems().add(operation.get()); | ||||
| 					break; | ||||
| 				case REMOVE: | ||||
| 					userList.getItems().removeIf(operation.get()::equals); | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| package envoy.client.util; | ||||
|  | ||||
| import java.util.logging.Level; | ||||
| import java.util.logging.*; | ||||
|  | ||||
| import javafx.scene.control.Alert; | ||||
| import javafx.scene.control.Alert.AlertType; | ||||
| @@ -26,7 +26,8 @@ import dev.kske.eventbus.EventBus; | ||||
|  */ | ||||
| public final class UserUtil { | ||||
|  | ||||
| 	private static final Context context = Context.getInstance(); | ||||
| 	private static final Context	context	= Context.getInstance(); | ||||
| 	private static final Logger		logger	= EnvoyLog.getLogger(UserUtil.class); | ||||
|  | ||||
| 	private UserUtil() {} | ||||
|  | ||||
| @@ -46,6 +47,7 @@ public final class UserUtil { | ||||
| 			EventBus.getInstance().dispatch(new EnvoyCloseEvent()); | ||||
| 			EventBus.getInstance().dispatch(new Logout()); | ||||
| 			context.getSceneContext().load(SceneInfo.LOGIN_SCENE); | ||||
| 			logger.log(Level.INFO, "A logout occurred."); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| @@ -63,6 +65,7 @@ public final class UserUtil { | ||||
| 		else { | ||||
| 			EventBus.getInstance().dispatch(new OwnStatusChange(newStatus)); | ||||
| 			if (context.getClient().isOnline()) context.getClient().send(new UserStatusChange(context.getLocalDB().getUser().getID(), newStatus)); | ||||
| 			logger.log(Level.INFO, "A manual status change occurred."); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -87,6 +90,7 @@ public final class UserUtil { | ||||
| 				context.getLocalDB().getChat(block.getID()).ifPresent(chat -> chat.setDisabled(true)); | ||||
| 				final var controller = context.getSceneContext().getController(); | ||||
| 				if (controller instanceof ChatScene) ((ChatScene) controller).disableChat(block, true); | ||||
| 				logger.log(Level.INFO, "A contact was blocked."); | ||||
| 			}); | ||||
| 		} | ||||
| 	} | ||||
| @@ -108,6 +112,7 @@ public final class UserUtil { | ||||
| 				context.getLocalDB().getChats().removeIf(chat -> chat.getRecipient().equals(delete)); | ||||
| 				if (context.getSceneContext().getController() instanceof ChatScene) | ||||
| 					((ChatScene) context.getSceneContext().getController()).resetState(); | ||||
| 				logger.log(Level.INFO, "A contact with all his messages was deleted."); | ||||
| 			}); | ||||
| 		} | ||||
| 	} | ||||
|   | ||||
There is an unnecessary space in the template.