Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Delete messages by swiping and add snackbar for undoing deletion. #99

Merged
merged 13 commits into from
Feb 18, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@
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;
Expand Down Expand Up @@ -374,15 +374,15 @@ private void scheduleDeletion(int position, Message message) {
}

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);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
Expand All @@ -16,4 +20,8 @@ public int getAllPosition() {
public int getAppPosition() {
return appPosition;
}

public Message getMessage() {
return message;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -34,13 +33,6 @@ public List<MessageWithImage> loadMore(Integer appId) {
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);
}
Expand All @@ -61,23 +53,21 @@ public int getLastReceivedMessage() {
}

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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<>();
Expand All @@ -31,11 +29,24 @@ synchronized void newMessages(Integer appId, PagedMessages pagedMessages) {
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) {
Expand Down Expand Up @@ -66,40 +77,48 @@ synchronized int getLastReceivedMessage() {
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() {
leopoldsedev marked this conversation as resolved.
Show resolved Hide resolved
return pendingDeletion != null;
}

private void addMessage(Message message, int allPosition, int appPosition) {
MessageState allMessages = state(MessageState.ALL_MESSAGES);
MessageState appMessages = state(message.getAppid());
Expand Down