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
### 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 {
}
}
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.
*/
......@@ -407,6 +418,11 @@ public class MethodCallHelper {
.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.
*/
......
......@@ -35,6 +35,8 @@ public interface RoomContract {
void showMessageSendFailure(Message message);
void showMessageDeleteFailure(Message message);
void autoloadImages();
void manualLoadImages();
......@@ -71,5 +73,7 @@ public interface RoomContract {
void refreshRoom();
void replyMessage(@NonNull Message message, boolean justQuote);
void acceptMessageDeleteFailure(Message message);
}
}
......@@ -647,6 +647,15 @@ public class RoomFragment extends AbstractChatRoomFragment implements
.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
public void autoloadImages() {
messageListAdapter.setAutoloadImages(true);
......@@ -679,6 +688,7 @@ public class RoomFragment extends AbstractChatRoomFragment implements
.setEditAction(this::onEditMessage)
.setCopyAction(msg -> onCopy(message.getMessage()))
.setQuoteAction(msg -> presenter.replyMessage(message, true))
.setDeleteAction(this::onDeleteMessage)
.showWith(context);
}
}
......@@ -688,6 +698,10 @@ public class RoomFragment extends AbstractChatRoomFragment implements
messageFormManager.setEditMessage(message.getMessage());
}
public void onDeleteMessage(Message message) {
presenter.deleteMessage(message);
}
private void showRoomListFragment(int actionId) {
//TODO: oddly sometimes getActivity() yields null. Investigate the situations this might happen
//and fix it, removing this null-check
......
......@@ -112,7 +112,11 @@ public class RoomPresenter extends BasePresenter<RoomContract.View>
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.
view.showMessageActions(message);
}
......@@ -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) {
if (currentRoom == null || message.getUser() == null) {
return "";
......
......@@ -44,7 +44,8 @@ public abstract class AbstractMessageViewHolder extends ModelViewHolder<PairedMe
* bind the view model.
*/
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);
} else {
errorImageView.setVisibility(View.GONE);
......
......@@ -37,7 +37,8 @@ public class MessagePopup {
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 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 Message message;
private CompositeDisposable compositeDisposable = new CompositeDisposable();
......@@ -73,6 +74,7 @@ public class MessagePopup {
.subscribe(
pair -> {
EDIT_ACTION_INFO.allowed = pair.second;
DELETE_ACTION_INFO.allowed = pair.second;
List<Action> allActions = singleton.defaultActions;
List<Action> allowedActions = new ArrayList<>(3);
for (int i = 0; i < allActions.size(); i++) {
......@@ -110,6 +112,7 @@ public class MessagePopup {
singleton.defaultActions.add(QUOTE_ACTION_INFO);
singleton.defaultActions.add(EDIT_ACTION_INFO);
singleton.defaultActions.add(COPY_ACTION_INFO);
singleton.defaultActions.add(DELETE_ACTION_INFO);
}
public static MessagePopup take(Message message) {
......@@ -163,6 +166,11 @@ public class MessagePopup {
return singleton;
}
public MessagePopup setDeleteAction(ActionListener actionListener) {
DELETE_ACTION_INFO.actionListener= actionListener;
return singleton;
}
public MessagePopup setQuoteAction(ActionListener actionListener) {
QUOTE_ACTION_INFO.actionListener = actionListener;
return singleton;
......
......@@ -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.UserDataSubscriber;
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.FileUploadingWithUfsObserver;
import chat.rocket.android.service.observer.GcmPushRegistrationObserver;
......@@ -57,6 +58,7 @@ public class RocketChatWebSocketThread extends HandlerThread {
LoadMessageProcedureObserver.class,
GetUsersOfRoomsProcedureObserver.class,
NewMessageObserver.class,
DeletedMessageObserver.class,
CurrentUserObserver.class,
FileUploadingToUrlObserver.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 @@
<string name="sending">Sending…</string>
<string name="not_synced">Not synced</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="discard">Discard</string>
<string name="ok">OK</string>
<plurals name="fmt_dialog_view_latest_message_title">
<item quantity="one">New %d message</item>
......
......@@ -5,6 +5,7 @@ import android.support.v4.util.Pair;
import com.hadisatrio.optional.Optional;
import chat.rocket.core.SyncState;
import io.reactivex.Flowable;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
......@@ -145,6 +146,8 @@ public class RealmMessageRepository extends RealmRepository implements MessageRe
}
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())
.isNotNull(RealmMessage.USER)
.findAllSorted(RealmMessage.TIMESTAMP, Sort.DESCENDING)
......
......@@ -8,4 +8,7 @@ public interface SyncState {
int SYNCING = 1;
int SYNCED = 2;
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,
}
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> {
......
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