Commit 30ab9216 authored by Leonardo Aramaki's avatar Leonardo Aramaki

Merge branch 'fix/notifications' of github.com:RocketChat/Rocket.Chat.Android...

Merge branch 'fix/notifications' of github.com:RocketChat/Rocket.Chat.Android into fix/notifications
parents 1aa9fa86 aa48875b
...@@ -19,4 +19,4 @@ git clone https://github.com/RocketChat/Rocket.Chat.Android ...@@ -19,4 +19,4 @@ git clone https://github.com/RocketChat/Rocket.Chat.Android
### Code style guide ### Code style guide
Before submitting a PR you should follow our [Coding Style](https://github.com/RocketChat/Rocket.Chat.Android/blob/develop/CODING_STYLE.md). Before submitting a PR you should follow our [Coding Style](https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md).
...@@ -394,6 +394,17 @@ public class MethodCallHelper { ...@@ -394,6 +394,17 @@ public class MethodCallHelper {
} }
} }
public Task<Void> deleteMessage(String messageID) {
try {
JSONObject messageJson = new JSONObject()
.put("_id", messageID);
return deleteMessage(messageJson);
} catch(JSONException exception) {
return Task.forError(exception);
}
}
/** /**
* Send message object. * Send message object.
*/ */
...@@ -407,6 +418,11 @@ public class MethodCallHelper { ...@@ -407,6 +418,11 @@ public class MethodCallHelper {
.onSuccessTask(task -> Task.forResult(null)); .onSuccessTask(task -> Task.forResult(null));
} }
private Task<Void> deleteMessage(final JSONObject messageJson) {
return call("deleteMessage", TIMEOUT_MS, () -> new JSONArray().put(messageJson))
.onSuccessTask(task -> Task.forResult(null));
}
/** /**
* mark all messages are read in the room. * mark all messages are read in the room.
*/ */
......
...@@ -35,6 +35,8 @@ public interface RoomContract { ...@@ -35,6 +35,8 @@ public interface RoomContract {
void showMessageSendFailure(Message message); void showMessageSendFailure(Message message);
void showMessageDeleteFailure(Message message);
void autoloadImages(); void autoloadImages();
void manualLoadImages(); void manualLoadImages();
...@@ -71,5 +73,7 @@ public interface RoomContract { ...@@ -71,5 +73,7 @@ public interface RoomContract {
void refreshRoom(); void refreshRoom();
void replyMessage(@NonNull Message message, boolean justQuote); void replyMessage(@NonNull Message message, boolean justQuote);
void acceptMessageDeleteFailure(Message message);
} }
} }
...@@ -647,6 +647,15 @@ public class RoomFragment extends AbstractChatRoomFragment implements ...@@ -647,6 +647,15 @@ public class RoomFragment extends AbstractChatRoomFragment implements
.show(); .show();
} }
@Override
public void showMessageDeleteFailure(Message message) {
new AlertDialog.Builder(getContext())
.setTitle(getContext().getString(R.string.failed_to_delete))
.setMessage(getContext().getString(R.string.failed_to_delete_message))
.setPositiveButton(R.string.ok, (dialog, which) -> presenter.acceptMessageDeleteFailure(message))
.show();
}
@Override @Override
public void autoloadImages() { public void autoloadImages() {
messageListAdapter.setAutoloadImages(true); messageListAdapter.setAutoloadImages(true);
...@@ -679,6 +688,7 @@ public class RoomFragment extends AbstractChatRoomFragment implements ...@@ -679,6 +688,7 @@ public class RoomFragment extends AbstractChatRoomFragment implements
.setEditAction(this::onEditMessage) .setEditAction(this::onEditMessage)
.setCopyAction(msg -> onCopy(message.getMessage())) .setCopyAction(msg -> onCopy(message.getMessage()))
.setQuoteAction(msg -> presenter.replyMessage(message, true)) .setQuoteAction(msg -> presenter.replyMessage(message, true))
.setDeleteAction(this::onDeleteMessage)
.showWith(context); .showWith(context);
} }
} }
...@@ -688,6 +698,10 @@ public class RoomFragment extends AbstractChatRoomFragment implements ...@@ -688,6 +698,10 @@ public class RoomFragment extends AbstractChatRoomFragment implements
messageFormManager.setEditMessage(message.getMessage()); messageFormManager.setEditMessage(message.getMessage());
} }
public void onDeleteMessage(Message message) {
presenter.deleteMessage(message);
}
private void showRoomListFragment(int actionId) { private void showRoomListFragment(int actionId) {
//TODO: oddly sometimes getActivity() yields null. Investigate the situations this might happen //TODO: oddly sometimes getActivity() yields null. Investigate the situations this might happen
//and fix it, removing this null-check //and fix it, removing this null-check
......
...@@ -112,7 +112,11 @@ public class RoomPresenter extends BasePresenter<RoomContract.View> ...@@ -112,7 +112,11 @@ public class RoomPresenter extends BasePresenter<RoomContract.View>
return; return;
} }
if (message.getType() == null && message.getSyncState() == SyncState.SYNCED) { if (message.getSyncState() == SyncState.DELETE_FAILED) {
view.showMessageDeleteFailure(message);
} else if (message.getSyncState() == SyncState.FAILED) {
view.showMessageSendFailure(message);
} else if (message.getType() == null && message.getSyncState() == SyncState.SYNCED) {
// If message is not a system message show applicable actions. // If message is not a system message show applicable actions.
view.showMessageActions(message); view.showMessageActions(message);
} }
...@@ -147,6 +151,15 @@ public class RoomPresenter extends BasePresenter<RoomContract.View> ...@@ -147,6 +151,15 @@ public class RoomPresenter extends BasePresenter<RoomContract.View>
); );
} }
public void acceptMessageDeleteFailure(Message message) {
final Disposable subscription = messageInteractor.acceptDeleteFailure(message)
.subscribeOn(AndroidSchedulers.from(BackgroundLooper.get()))
.observeOn(AndroidSchedulers.mainThread())
.subscribe();
addSubscription(subscription);
}
private String buildReplyOrQuoteMarkdown(String baseUrl, Message message, boolean justQuote) { private String buildReplyOrQuoteMarkdown(String baseUrl, Message message, boolean justQuote) {
if (currentRoom == null || message.getUser() == null) { if (currentRoom == null || message.getUser() == null) {
return ""; return "";
......
...@@ -44,7 +44,8 @@ public abstract class AbstractMessageViewHolder extends ModelViewHolder<PairedMe ...@@ -44,7 +44,8 @@ public abstract class AbstractMessageViewHolder extends ModelViewHolder<PairedMe
* bind the view model. * bind the view model.
*/ */
public final void bind(PairedMessage pairedMessage, boolean autoloadImages) { public final void bind(PairedMessage pairedMessage, boolean autoloadImages) {
if (pairedMessage.target.getSyncState() == SyncState.FAILED) { if (pairedMessage.target.getSyncState() == SyncState.FAILED ||
pairedMessage.target.getSyncState() == SyncState.DELETE_FAILED) {
errorImageView.setVisibility(View.VISIBLE); errorImageView.setVisibility(View.VISIBLE);
} else { } else {
errorImageView.setVisibility(View.GONE); errorImageView.setVisibility(View.GONE);
......
...@@ -37,7 +37,8 @@ public class MessagePopup { ...@@ -37,7 +37,8 @@ public class MessagePopup {
private static final Action QUOTE_ACTION_INFO = new Action("Quote", null, true); private static final Action QUOTE_ACTION_INFO = new Action("Quote", null, true);
private static final Action EDIT_ACTION_INFO = new Action("Edit", null, true); private static final Action EDIT_ACTION_INFO = new Action("Edit", null, true);
private static final Action COPY_ACTION_INFO = new Action("Copy", null, true); private static final Action COPY_ACTION_INFO = new Action("Copy", null, true);
private final List<Action> defaultActions = new ArrayList<>(4); private static final Action DELETE_ACTION_INFO = new Action("Delete", null, false);
private final List<Action> defaultActions = new ArrayList<>(5);
private final List<Action> otherActions = new ArrayList<>(); private final List<Action> otherActions = new ArrayList<>();
private Message message; private Message message;
private CompositeDisposable compositeDisposable = new CompositeDisposable(); private CompositeDisposable compositeDisposable = new CompositeDisposable();
...@@ -73,6 +74,7 @@ public class MessagePopup { ...@@ -73,6 +74,7 @@ public class MessagePopup {
.subscribe( .subscribe(
pair -> { pair -> {
EDIT_ACTION_INFO.allowed = pair.second; EDIT_ACTION_INFO.allowed = pair.second;
DELETE_ACTION_INFO.allowed = pair.second;
List<Action> allActions = singleton.defaultActions; List<Action> allActions = singleton.defaultActions;
List<Action> allowedActions = new ArrayList<>(3); List<Action> allowedActions = new ArrayList<>(3);
for (int i = 0; i < allActions.size(); i++) { for (int i = 0; i < allActions.size(); i++) {
...@@ -110,6 +112,7 @@ public class MessagePopup { ...@@ -110,6 +112,7 @@ public class MessagePopup {
singleton.defaultActions.add(QUOTE_ACTION_INFO); singleton.defaultActions.add(QUOTE_ACTION_INFO);
singleton.defaultActions.add(EDIT_ACTION_INFO); singleton.defaultActions.add(EDIT_ACTION_INFO);
singleton.defaultActions.add(COPY_ACTION_INFO); singleton.defaultActions.add(COPY_ACTION_INFO);
singleton.defaultActions.add(DELETE_ACTION_INFO);
} }
public static MessagePopup take(Message message) { public static MessagePopup take(Message message) {
...@@ -163,6 +166,11 @@ public class MessagePopup { ...@@ -163,6 +166,11 @@ public class MessagePopup {
return singleton; return singleton;
} }
public MessagePopup setDeleteAction(ActionListener actionListener) {
DELETE_ACTION_INFO.actionListener= actionListener;
return singleton;
}
public MessagePopup setQuoteAction(ActionListener actionListener) { public MessagePopup setQuoteAction(ActionListener actionListener) {
QUOTE_ACTION_INFO.actionListener = actionListener; QUOTE_ACTION_INFO.actionListener = actionListener;
return singleton; return singleton;
......
...@@ -23,6 +23,7 @@ import chat.rocket.android.service.ddp.base.ActiveUsersSubscriber; ...@@ -23,6 +23,7 @@ import chat.rocket.android.service.ddp.base.ActiveUsersSubscriber;
import chat.rocket.android.service.ddp.base.LoginServiceConfigurationSubscriber; import chat.rocket.android.service.ddp.base.LoginServiceConfigurationSubscriber;
import chat.rocket.android.service.ddp.base.UserDataSubscriber; import chat.rocket.android.service.ddp.base.UserDataSubscriber;
import chat.rocket.android.service.observer.CurrentUserObserver; import chat.rocket.android.service.observer.CurrentUserObserver;
import chat.rocket.android.service.observer.DeletedMessageObserver;
import chat.rocket.android.service.observer.FileUploadingToUrlObserver; import chat.rocket.android.service.observer.FileUploadingToUrlObserver;
import chat.rocket.android.service.observer.FileUploadingWithUfsObserver; import chat.rocket.android.service.observer.FileUploadingWithUfsObserver;
import chat.rocket.android.service.observer.GcmPushRegistrationObserver; import chat.rocket.android.service.observer.GcmPushRegistrationObserver;
...@@ -57,6 +58,7 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -57,6 +58,7 @@ public class RocketChatWebSocketThread extends HandlerThread {
LoadMessageProcedureObserver.class, LoadMessageProcedureObserver.class,
GetUsersOfRoomsProcedureObserver.class, GetUsersOfRoomsProcedureObserver.class,
NewMessageObserver.class, NewMessageObserver.class,
DeletedMessageObserver.class,
CurrentUserObserver.class, CurrentUserObserver.class,
FileUploadingToUrlObserver.class, FileUploadingToUrlObserver.class,
FileUploadingWithUfsObserver.class, FileUploadingWithUfsObserver.class,
......
package chat.rocket.android.service.observer;
import android.content.Context;
import org.json.JSONObject;
import java.util.List;
import chat.rocket.android.api.MethodCallHelper;
import chat.rocket.android.helper.LogIfError;
import chat.rocket.android.log.RCLog;
import chat.rocket.android.service.DDPClientRef;
import chat.rocket.core.SyncState;
import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.persistence.realm.models.ddp.RealmMessage;
import io.realm.Realm;
import io.realm.RealmResults;
/**
* Observe messages for deletion.
*/
public class DeletedMessageObserver extends AbstractModelObserver<RealmMessage> {
private final MethodCallHelper methodCall;
public DeletedMessageObserver(Context context, String hostname, RealmHelper realmHelper, DDPClientRef ddpClientRef) {
super(context, hostname, realmHelper, ddpClientRef);
methodCall = new MethodCallHelper(realmHelper, ddpClientRef);
realmHelper.executeTransaction(realm -> {
// resume pending operations.
RealmResults<RealmMessage> pendingMethodCalls = realm.where(RealmMessage.class)
.equalTo(RealmMessage.SYNC_STATE, SyncState.DELETING)
.findAll();
for (RealmMessage message : pendingMethodCalls) {
message.setSyncState(SyncState.DELETE_NOT_SYNCED);
}
return null;
}).continueWith(new LogIfError());
}
@Override
public RealmResults<RealmMessage> queryItems(Realm realm) {
return realm.where(RealmMessage.class)
.equalTo(RealmMessage.SYNC_STATE, SyncState.DELETE_NOT_SYNCED)
.isNotNull(RealmMessage.ROOM_ID)
.findAll();
}
@Override
public void onUpdateResults(List<RealmMessage> results) {
if (results.isEmpty()) {
return;
}
for(RealmMessage message : results) {
final String messageId = message.getId();
realmHelper.executeTransaction(realm ->
realm.createOrUpdateObjectFromJson(RealmMessage.class, new JSONObject()
.put(RealmMessage.ID, messageId)
.put(RealmMessage.SYNC_STATE, SyncState.DELETING)
)
).onSuccessTask(task -> methodCall.deleteMessage(messageId)
).continueWith(task -> {
if(task.isFaulted()) {
RCLog.w(task.getError());
realmHelper.executeTransaction(realm ->
realm.createOrUpdateObjectFromJson(RealmMessage.class, new JSONObject()
.put(RealmMessage.ID, messageId)
.put(RealmMessage.SYNC_STATE, SyncState.DELETE_FAILED)));
} else {
realmHelper.executeTransaction(realm ->
realm.where(RealmMessage.class)
.equalTo(RealmMessage.ID, messageId)
.findAll()
.deleteAllFromRealm()
);
}
return null;
});
}
}
}
...@@ -27,8 +27,11 @@ ...@@ -27,8 +27,11 @@
<string name="sending">Sending…</string> <string name="sending">Sending…</string>
<string name="not_synced">Not synced</string> <string name="not_synced">Not synced</string>
<string name="failed_to_sync">Failed to sync</string> <string name="failed_to_sync">Failed to sync</string>
<string name="failed_to_delete">Failed to delete</string>
<string name="failed_to_delete_message">The message could not be deleted at this time. Please try again later</string>
<string name="resend">Resend</string> <string name="resend">Resend</string>
<string name="discard">Discard</string> <string name="discard">Discard</string>
<string name="ok">OK</string>
<plurals name="fmt_dialog_view_latest_message_title"> <plurals name="fmt_dialog_view_latest_message_title">
<item quantity="one">New %d message</item> <item quantity="one">New %d message</item>
......
...@@ -5,6 +5,7 @@ import android.support.v4.util.Pair; ...@@ -5,6 +5,7 @@ import android.support.v4.util.Pair;
import com.hadisatrio.optional.Optional; import com.hadisatrio.optional.Optional;
import chat.rocket.core.SyncState;
import io.reactivex.Flowable; import io.reactivex.Flowable;
import io.reactivex.Single; import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
...@@ -145,6 +146,8 @@ public class RealmMessageRepository extends RealmRepository implements MessageRe ...@@ -145,6 +146,8 @@ public class RealmMessageRepository extends RealmRepository implements MessageRe
} }
return RxJavaInterop.toV2Flowable(pair.first.where(RealmMessage.class) return RxJavaInterop.toV2Flowable(pair.first.where(RealmMessage.class)
.notEqualTo(RealmMessage.SYNC_STATE, SyncState.DELETE_NOT_SYNCED)
.notEqualTo(RealmMessage.SYNC_STATE, SyncState.DELETING)
.equalTo(RealmMessage.ROOM_ID, room.getRoomId()) .equalTo(RealmMessage.ROOM_ID, room.getRoomId())
.isNotNull(RealmMessage.USER) .isNotNull(RealmMessage.USER)
.findAllSorted(RealmMessage.TIMESTAMP, Sort.DESCENDING) .findAllSorted(RealmMessage.TIMESTAMP, Sort.DESCENDING)
......
...@@ -8,4 +8,7 @@ public interface SyncState { ...@@ -8,4 +8,7 @@ public interface SyncState {
int SYNCING = 1; int SYNCING = 1;
int SYNCED = 2; int SYNCED = 2;
int FAILED = 3; int FAILED = 3;
int DELETE_NOT_SYNCED = 4;
int DELETING = 5;
int DELETE_FAILED = 6;
} }
...@@ -76,7 +76,14 @@ class MessageInteractor(private val messageRepository: MessageRepository, ...@@ -76,7 +76,14 @@ class MessageInteractor(private val messageRepository: MessageRepository,
} }
fun delete(message: Message): Single<Boolean> { fun delete(message: Message): Single<Boolean> {
return messageRepository.delete(message) return messageRepository.save(message.withSyncState(SyncState.DELETE_NOT_SYNCED))
}
/**
* Resets the message syncstate to SYNCED after a user has accepted a failed delete
*/
fun acceptDeleteFailure(message: Message): Single<Boolean> {
return messageRepository.save(message.withSyncState(SyncState.SYNCED))
} }
fun unreadCountFor(room: Room, user: User): Single<Int> { fun unreadCountFor(room: Room, user: User): Single<Int> {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment