Auto mark dm seen if enabled in settings. Fixes https://github.com/austinhuang0131/barinsta/issues/797

This commit is contained in:
Ammar Githam 2021-03-18 01:41:29 +09:00
parent 321af4cad4
commit dfa3a50893
8 changed files with 213 additions and 15 deletions

View File

@ -9,6 +9,7 @@ import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import java.util.List;
import java.util.Objects;
import awais.instagrabber.adapters.viewholder.directmessages.DirectInboxItemViewHolder;
import awais.instagrabber.databinding.LayoutDmInboxItemBinding;
@ -29,6 +30,8 @@ public final class DirectMessageInboxAdapter extends ListAdapter<DirectThread, D
@NonNull final DirectThread newThread) {
final boolean titleEqual = oldThread.getThreadTitle().equals(newThread.getThreadTitle());
if (!titleEqual) return false;
final boolean lastSeenAtEqual = Objects.equals(oldThread.getLastSeenAt(), newThread.getLastSeenAt());
if (!lastSeenAtEqual) return false;
final List<DirectItem> oldItems = oldThread.getItems();
final List<DirectItem> newItems = newThread.getItems();
if (oldItems == null || newItems == null) return false;

View File

@ -58,7 +58,6 @@ import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import awais.instagrabber.ProfileNavGraphDirections;
import awais.instagrabber.R;
@ -100,6 +99,7 @@ import awais.instagrabber.repositories.responses.directmessages.DirectItemVisual
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient;
import awais.instagrabber.utils.AppExecutors;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.DownloadUtils;
import awais.instagrabber.utils.PermissionUtils;
import awais.instagrabber.utils.ResponseBodyUtils;
@ -146,6 +146,17 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
private int prevLength;
private BadgeDrawable pendingRequestCountBadgeDrawable;
private boolean isPendingRequestCountBadgeAttached = false;
private ItemTouchHelper itemTouchHelper;
private LiveData<Boolean> pendingLiveData;
private LiveData<DirectThread> threadLiveData;
private LiveData<Integer> inputModeLiveData;
private LiveData<String> threadTitleLiveData;
private LiveData<Resource<Object>> fetchingLiveData;
private LiveData<List<DirectItem>> itemsLiveData;
private LiveData<DirectItem> replyToItemLiveData;
private LiveData<Integer> pendingRequestsCountLiveData;
private LiveData<List<User>> usersLiveData;
private boolean autoMarkAsSeen = false;
private final AppExecutors appExecutors = AppExecutors.getInstance();
private final Animatable2Compat.AnimationCallback micToSendAnimationCallback = new Animatable2Compat.AnimationCallback() {
@ -306,22 +317,13 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
backStackSavedStateResultLiveData.postValue(null);
};
private final MutableLiveData<Integer> inputLength = new MutableLiveData<>(0);
private ItemTouchHelper itemTouchHelper;
private LiveData<Boolean> pendingLiveData;
private LiveData<DirectThread> threadLiveData;
private LiveData<Integer> inputModeLiveData;
private LiveData<String> threadTitleLiveData;
private LiveData<Resource<Object>> fetchingLiveData;
private LiveData<List<DirectItem>> itemsLiveData;
private LiveData<DirectItem> replyToItemLiveData;
private LiveData<Integer> pendingRequestsCountLiveData;
private LiveData<List<User>> usersLiveData;
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
fragmentActivity = (MainActivity) requireActivity();
appStateViewModel = new ViewModelProvider(fragmentActivity).get(AppStateViewModel.class);
autoMarkAsSeen = Utils.settingsHelper.getBoolean(Constants.DM_MARK_AS_SEEN);
final Bundle arguments = getArguments();
if (arguments == null) return;
final DirectMessageThreadFragmentArgs fragmentArgs = DirectMessageThreadFragmentArgs.fromBundle(arguments);
@ -895,6 +897,9 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
}
private void submitItemsToAdapter(final List<DirectItem> items) {
if (autoMarkAsSeen) {
binding.chats.post(() -> viewModel.markAsSeen());
}
if (itemsAdapter == null) return;
itemsAdapter.submitList(items, () -> {
itemOrHeaders = itemsAdapter.getList();

View File

@ -22,6 +22,7 @@ import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
@ -45,12 +46,15 @@ import awais.instagrabber.repositories.responses.directmessages.DirectInbox;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectItemEmojiReaction;
import awais.instagrabber.repositories.responses.directmessages.DirectItemReactions;
import awais.instagrabber.repositories.responses.directmessages.DirectItemSeenResponse;
import awais.instagrabber.repositories.responses.directmessages.DirectItemSeenResponse.DirectItemSeenResponsePayload;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroadcastResponse;
import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroadcastResponseMessageMetadata;
import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroadcastResponsePayload;
import awais.instagrabber.repositories.responses.directmessages.DirectThreadDetailsChangeResponse;
import awais.instagrabber.repositories.responses.directmessages.DirectThreadFeedResponse;
import awais.instagrabber.repositories.responses.directmessages.DirectThreadLastSeenAt;
import awais.instagrabber.repositories.responses.directmessages.DirectThreadParticipantRequestsResponse;
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient;
import awais.instagrabber.repositories.responses.giphy.GiphyGif;
@ -1187,7 +1191,7 @@ public final class ThreadManager {
private void handleErrorBody(@NonNull final Call<?> call,
@NonNull final Response<?> response,
@NonNull final MutableLiveData<Resource<Object>> data) {
final MutableLiveData<Resource<Object>> data) {
try {
final String string = response.errorBody() != null ? response.errorBody().string() : "";
final String msg = String.format(Locale.US,
@ -1195,10 +1199,14 @@ public final class ThreadManager {
call.request().url().toString(),
response.code(),
string);
data.postValue(Resource.error(msg, null));
if (data != null) {
data.postValue(Resource.error(msg, null));
}
Log.e(TAG, msg);
} catch (IOException e) {
data.postValue(Resource.error(e.getMessage(), null));
if (data != null) {
data.postValue(Resource.error(e.getMessage(), null));
}
Log.e(TAG, "onResponse: ", e);
}
}
@ -1794,6 +1802,39 @@ public final class ThreadManager {
return inviter;
}
public void markAsSeen(@NonNull final DirectItem directItem) {
final Call<DirectItemSeenResponse> request = service.markAsSeen(threadId, directItem);
request.enqueue(new Callback<DirectItemSeenResponse>() {
@Override
public void onResponse(@NonNull final Call<DirectItemSeenResponse> call,
@NonNull final Response<DirectItemSeenResponse> response) {
if (!response.isSuccessful()) {
handleErrorBody(call, response, null);
return;
}
final DirectItemSeenResponse seenResponse = response.body();
if (seenResponse == null) return;
inboxManager.fetchUnseenCount();
final DirectItemSeenResponsePayload payload = seenResponse.getPayload();
if (payload == null) return;
final String timestamp = payload.getTimestamp();
final DirectThread thread = ThreadManager.this.thread.getValue();
if (thread == null) return;
Map<Long, DirectThreadLastSeenAt> lastSeenAt = thread.getLastSeenAt();
lastSeenAt = lastSeenAt == null ? new HashMap<>() : new HashMap<>(lastSeenAt);
lastSeenAt.put(currentUser.getPk(), new DirectThreadLastSeenAt(timestamp, directItem.getItemId()));
thread.setLastSeenAt(lastSeenAt);
setThread(thread, true);
}
@Override
public void onFailure(@NonNull final Call<DirectItemSeenResponse> call,
@NonNull final Throwable t) {
Log.e(TAG, "onFailure: ", t);
}
});
}
private interface OnSuccessAction {
void onSuccess();
}

View File

@ -4,6 +4,7 @@ import java.util.Map;
import awais.instagrabber.repositories.responses.directmessages.DirectBadgeCount;
import awais.instagrabber.repositories.responses.directmessages.DirectInboxResponse;
import awais.instagrabber.repositories.responses.directmessages.DirectItemSeenResponse;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroadcastResponse;
import awais.instagrabber.repositories.responses.directmessages.DirectThreadDetailsChangeResponse;
@ -145,4 +146,10 @@ public interface DirectMessagesRepository {
@POST("/api/v1/direct_v2/threads/{threadId}/decline/")
Call<String> declineRequest(@Path("threadId") String threadId,
@FieldMap final Map<String, String> form);
@FormUrlEncoded
@POST("/api/v1/direct_v2/threads/{threadId}/items/{itemId}/seen/")
Call<DirectItemSeenResponse> markItemSeen(@Path("threadId") String threadId,
@Path("itemId") String itemId,
@FieldMap final Map<String, String> form);
}

View File

@ -0,0 +1,95 @@
package awais.instagrabber.repositories.responses.directmessages;
import androidx.annotation.NonNull;
import java.util.Objects;
public class DirectItemSeenResponse {
private final String action;
private final DirectItemSeenResponsePayload payload;
private final String status;
public DirectItemSeenResponse(final String action, final DirectItemSeenResponsePayload payload, final String status) {
this.action = action;
this.payload = payload;
this.status = status;
}
public String getAction() {
return action;
}
public DirectItemSeenResponsePayload getPayload() {
return payload;
}
public String getStatus() {
return status;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final DirectItemSeenResponse that = (DirectItemSeenResponse) o;
return Objects.equals(action, that.action) &&
Objects.equals(payload, that.payload) &&
Objects.equals(status, that.status);
}
@Override
public int hashCode() {
return Objects.hash(action, payload, status);
}
@NonNull
@Override
public String toString() {
return "DirectItemSeenResponse{" +
"action='" + action + '\'' +
", payload=" + payload +
", status='" + status + '\'' +
'}';
}
public static class DirectItemSeenResponsePayload {
private final int count;
private final String timestamp;
public DirectItemSeenResponsePayload(final int count, final String timestamp) {
this.count = count;
this.timestamp = timestamp;
}
public int getCount() {
return count;
}
public String getTimestamp() {
return timestamp;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final DirectItemSeenResponsePayload that = (DirectItemSeenResponsePayload) o;
return count == that.count &&
Objects.equals(timestamp, that.timestamp);
}
@Override
public int hashCode() {
return Objects.hash(count, timestamp);
}
@NonNull
@Override
public String toString() {
return "DirectItemSeenResponsePayload{" +
"count=" + count +
", timestamp='" + timestamp + '\'' +
'}';
}
}
}

View File

@ -36,7 +36,7 @@ public class DirectThread implements Serializable, Cloneable {
private final User inviter;
private final boolean hasOlder;
private final boolean hasNewer;
private final Map<Long, DirectThreadLastSeenAt> lastSeenAt;
private Map<Long, DirectThreadLastSeenAt> lastSeenAt;
private final String newestCursor;
private final String oldestCursor;
private final boolean isSpam;
@ -248,6 +248,10 @@ public class DirectThread implements Serializable, Cloneable {
return lastSeenAt;
}
public void setLastSeenAt(final Map<Long, DirectThreadLastSeenAt> lastSeenAt) {
this.lastSeenAt = lastSeenAt;
}
public String getNewestCursor() {
return newestCursor;
}

View File

@ -15,6 +15,9 @@ import androidx.lifecycle.Transformations;
import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@ -25,6 +28,7 @@ import awais.instagrabber.models.Resource;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.repositories.responses.directmessages.DirectThreadLastSeenAt;
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient;
import awais.instagrabber.repositories.responses.giphy.GiphyGif;
import awais.instagrabber.utils.Constants;
@ -273,4 +277,28 @@ public class DirectThreadViewModel extends AndroidViewModel {
public LiveData<Resource<Object>> declineRequest() {
return threadManager.declineRequest();
}
public void markAsSeen() {
final DirectThread thread = getThread().getValue();
if (thread == null) return;
final List<DirectItem> items = thread.getItems();
if (items == null || items.isEmpty()) return;
final Optional<DirectItem> itemOptional = items.stream()
.filter(item -> item.getUserId() != currentUser.getPk())
.findFirst();
if (!itemOptional.isPresent()) return;
final DirectItem directItem = itemOptional.get();
final Map<Long, DirectThreadLastSeenAt> lastSeenAt = thread.getLastSeenAt();
if (lastSeenAt != null) {
final DirectThreadLastSeenAt seenAt = lastSeenAt.get(currentUser.getPk());
try {
if (seenAt != null
&& (Objects.equals(seenAt.getItemId(), directItem.getItemId())
|| Long.parseLong(seenAt.getTimestamp()) >= directItem.getTimestamp())) {
return;
}
} catch (Exception ignored) {}
}
threadManager.markAsSeen(directItem);
}
}

View File

@ -29,6 +29,8 @@ import awais.instagrabber.repositories.requests.directmessages.VideoBroadcastOpt
import awais.instagrabber.repositories.requests.directmessages.VoiceBroadcastOptions;
import awais.instagrabber.repositories.responses.directmessages.DirectBadgeCount;
import awais.instagrabber.repositories.responses.directmessages.DirectInboxResponse;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectItemSeenResponse;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroadcastResponse;
import awais.instagrabber.repositories.responses.directmessages.DirectThreadDetailsChangeResponse;
@ -448,4 +450,17 @@ public class DirectMessagesService extends BaseService {
);
return repository.declineRequest(threadId, form);
}
public Call<DirectItemSeenResponse> markAsSeen(@NonNull final String threadId,
@NonNull final DirectItem directItem) {
final ImmutableMap<String, String> form = ImmutableMap.<String, String>builder()
.put("_csrftoken", csrfToken)
.put("_uuid", deviceUuid)
.put("use_unified_inbox", "true")
.put("action", "mark_seen")
.put("thread_id", threadId)
.put("item_id", directItem.getItemId())
.build();
return repository.markItemSeen(threadId, directItem.getItemId(), form);
}
}