Rework undo deletion logic.

Previously the MessageFacade and MessageStateHolder both had their own
state of the last deleted message, which was redundant. Now only
MessageStateHolder governs the state of a pending deletion and
MessageFacade handles commiting the deletion to the server.
This commit is contained in:
leopoldsedev
2020-02-15 19:12:35 +01:00
committed by Jannis Mattheis
parent 0ebeeec35e
commit 8d4a331bba
4 changed files with 60 additions and 43 deletions

View File

@@ -55,10 +55,10 @@ import com.github.gotify.log.Log;
import com.github.gotify.log.LogsActivity;
import com.github.gotify.login.LoginActivity;
import com.github.gotify.messages.provider.ApplicationHolder;
import com.github.gotify.messages.provider.MessageDeletion;
import com.github.gotify.messages.provider.MessageFacade;
import com.github.gotify.messages.provider.MessageState;
import com.github.gotify.messages.provider.MessageWithImage;
import com.github.gotify.messages.provider.PositionPair;
import com.github.gotify.service.WebSocketService;
import com.google.android.material.navigation.NavigationView;
import com.google.android.material.snackbar.BaseTransientBottomBar;
@@ -374,15 +374,15 @@ public class MessagesActivity extends AppCompatActivity
}
private void undoDelete() {
PositionPair positionPair = messages.undoDeleteLocal();
MessageDeletion deletion = messages.undoDeleteLocal();
if (positionPair != null) {
if (deletion != null) {
ListMessageAdapter adapter = (ListMessageAdapter) messagesView.getAdapter();
adapter.setItems(messages.get(appId));
int insertPosition =
appId == MessageState.ALL_MESSAGES
? positionPair.getAllPosition()
: positionPair.getAppPosition();
? deletion.getAllPosition()
: deletion.getAppPosition();
adapter.notifyItemInserted(insertPosition);
}
}

View File

@@ -1,10 +1,14 @@
package com.github.gotify.messages.provider;
public final class PositionPair {
import com.github.gotify.client.model.Message;
public final class MessageDeletion {
private final Message message;
private final int allPosition;
private final int appPosition;
public PositionPair(int allPosition, int appPosition) {
public MessageDeletion(Message message, int allPosition, int appPosition) {
this.message = message;
this.allPosition = allPosition;
this.appPosition = appPosition;
}
@@ -16,4 +20,8 @@ public final class PositionPair {
public int getAppPosition() {
return appPosition;
}
public Message getMessage() {
return message;
}
}

View File

@@ -10,7 +10,6 @@ public class MessageFacade {
private final MessageRequester requester;
private final MessageStateHolder state;
private final MessageImageCombiner combiner;
private Message messagePendingDeletion;
public MessageFacade(MessageApi api, ApplicationHolder applicationHolder) {
this.applicationHolder = applicationHolder;
@@ -34,13 +33,6 @@ public class MessageFacade {
if (state.hasNext || !state.loaded) {
PagedMessages pagedMessages = requester.loadMore(state);
this.state.newMessages(appId, pagedMessages);
// If there is a message with pending removal, it should not reappear in the list when
// reloading. Thus, it needs to be removed from the local list again after loading new
// messages.
if (messagePendingDeletion != null) {
this.state.removeMessage(messagePendingDeletion);
}
}
return get(appId);
}
@@ -61,23 +53,21 @@ public class MessageFacade {
}
public synchronized void deleteLocal(Message message) {
this.state.removeMessage(message);
// If there is already a deletion pending, that one should be executed before scheduling the
// next deletion.
if (messagePendingDeletion != null) {
commitDelete();
}
messagePendingDeletion = message;
if (this.state.deletionPending()) commitDelete();
this.state.deleteMessage(message);
}
public synchronized void commitDelete() {
this.requester.asyncRemoveMessage(messagePendingDeletion);
messagePendingDeletion = null;
if (this.state.deletionPending()) {
MessageDeletion deletion = this.state.purgePendingDeletion();
this.requester.asyncRemoveMessage(deletion.getMessage());
}
}
public synchronized PositionPair undoDeleteLocal() {
messagePendingDeletion = null;
return this.state.undoLastRemoveMessage();
public synchronized MessageDeletion undoDeleteLocal() {
return this.state.undoPendingDeletion();
}
public boolean deleteAll(Integer appId) {

View File

@@ -9,9 +9,7 @@ class MessageStateHolder {
private int lastReceivedMessage = -1;
private Map<Integer, MessageState> states = new HashMap<>();
private Message lastRemovedMessage;
private int lastRemovedAllPosition;
private int lastRemovedAppPosition;
private MessageDeletion pendingDeletion = null;
synchronized void clear() {
states = new HashMap<>();
@@ -31,11 +29,24 @@ class MessageStateHolder {
state.nextSince = pagedMessages.getPaging().getSince();
state.appId = appId;
states.put(appId, state);
// If there is a message with pending deletion, it should not reappear in the list in case
// it is added again.
if (deletionPending()) {
deleteMessage(pendingDeletion.getMessage());
}
}
synchronized void newMessage(Message message) {
// If there is a message with pending deletion, its indices are going to change. To keep
// them consistent the deletion is undone first and redone again after adding the new
// message.
MessageDeletion deletion = undoPendingDeletion();
addMessage(message, 0, 0);
lastReceivedMessage = message.getId();
if (deletion != null) deleteMessage(deletion.getMessage());
}
synchronized MessageState state(Integer appId) {
@@ -66,40 +77,48 @@ class MessageStateHolder {
return lastReceivedMessage;
}
synchronized void removeMessage(Message message) {
synchronized void deleteMessage(Message message) {
MessageState allMessages = state(MessageState.ALL_MESSAGES);
MessageState appMessages = state(message.getAppid());
int pendingDeletedAllPosition = -1;
int pendingDeletedAppPosition = -1;
if (allMessages.loaded) {
int allPosition = allMessages.messages.indexOf(message);
allMessages.messages.remove(allPosition);
lastRemovedAllPosition = allPosition;
pendingDeletedAllPosition = allPosition;
}
if (appMessages.loaded) {
int appPosition = appMessages.messages.indexOf(message);
appMessages.messages.remove(appPosition);
lastRemovedAppPosition = appPosition;
pendingDeletedAppPosition = appPosition;
}
lastRemovedMessage = message;
pendingDeletion =
new MessageDeletion(message, pendingDeletedAllPosition, pendingDeletedAppPosition);
}
synchronized PositionPair undoLastRemoveMessage() {
PositionPair result = null;
if (lastRemovedMessage != null) {
addMessage(lastRemovedMessage, lastRemovedAllPosition, lastRemovedAppPosition);
result = new PositionPair(lastRemovedAllPosition, lastRemovedAppPosition);
lastRemovedMessage = null;
lastRemovedAllPosition = -1;
lastRemovedAppPosition = -1;
}
synchronized MessageDeletion undoPendingDeletion() {
if (pendingDeletion != null)
addMessage(
pendingDeletion.getMessage(),
pendingDeletion.getAllPosition(),
pendingDeletion.getAppPosition());
return purgePendingDeletion();
}
synchronized MessageDeletion purgePendingDeletion() {
MessageDeletion result = pendingDeletion;
pendingDeletion = null;
return result;
}
boolean deletionPending() {
return pendingDeletion != null;
}
private void addMessage(Message message, int allPosition, int appPosition) {
MessageState allMessages = state(MessageState.ALL_MESSAGES);
MessageState appMessages = state(message.getAppid());