diff --git a/app/src/main/java/awais/instagrabber/adapters/CommentsAdapter.java b/app/src/main/java/awais/instagrabber/adapters/CommentsAdapter.java index d4de8ca6..58f7d1fa 100755 --- a/app/src/main/java/awais/instagrabber/adapters/CommentsAdapter.java +++ b/app/src/main/java/awais/instagrabber/adapters/CommentsAdapter.java @@ -17,7 +17,7 @@ public final class CommentsAdapter extends ListAdapter DIFF_CALLBACK = new DiffUtil.ItemCallback() { @Override public boolean areItemsTheSame(@NonNull final Comment oldItem, @NonNull final Comment newItem) { - return Objects.equals(oldItem.getId(), newItem.getId()); + return Objects.equals(oldItem.getPk(), newItem.getPk()); } @Override diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/CommentViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/CommentViewHolder.java index 151d308c..0643f7cd 100644 --- a/app/src/main/java/awais/instagrabber/adapters/viewholder/CommentViewHolder.java +++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/CommentViewHolder.java @@ -123,7 +123,7 @@ public final class CommentViewHolder extends RecyclerView.ViewHolder { private void setLikes(@NonNull final Comment comment, final boolean isReply) { // final String likesString = itemView.getResources().getQuantityString(R.plurals.likes_count, likes, likes); - binding.likes.setText(String.valueOf(comment.getLikes())); + binding.likes.setText(String.valueOf(comment.getCommentLikeCount())); binding.likes.setOnLongClickListener(v -> { if (commentCallback == null) return false; commentCallback.onViewLikes(comment); @@ -147,7 +147,7 @@ public final class CommentViewHolder extends RecyclerView.ViewHolder { } private void setReplies(@NonNull final Comment comment, final boolean isReply) { - final int replies = comment.getReplyCount(); + final int replies = comment.getChildCommentCount(); binding.replies.setVisibility(View.VISIBLE); final String text = isReply ? "" : String.valueOf(replies); // final String string = itemView.getResources().getQuantityString(R.plurals.replies_count, replies, replies); diff --git a/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java b/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java index 8916fb38..fd050345 100644 --- a/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java @@ -419,7 +419,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR final long locationId = locationModel.getPk(); // binding.swipeRefreshLayout.setRefreshing(true); locationDetailsBinding.mainLocationImage.setImageURI("res:/" + R.drawable.ic_location); - // final String postCount = String.valueOf(locationModel.getCount()); + // final String postCount = String.valueOf(locationModel.getChildCommentCount()); // final SpannableStringBuilder span = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.main_posts_count_inline, // locationModel.getPostCount() > 2000000000L // ? 2000000000 diff --git a/app/src/main/java/awais/instagrabber/fragments/comments/Helper.java b/app/src/main/java/awais/instagrabber/fragments/comments/Helper.java index 823ffb33..c3fc3f32 100644 --- a/app/src/main/java/awais/instagrabber/fragments/comments/Helper.java +++ b/app/src/main/java/awais/instagrabber/fragments/comments/Helper.java @@ -126,7 +126,7 @@ public final class Helper { if (navController == null) return; try { final Bundle bundle = new Bundle(); - bundle.putString("postId", comment.getId()); + bundle.putString("postId", comment.getPk()); bundle.putBoolean("isComment", true); navController.navigate(R.id.action_global_likesViewerFragment, bundle); } catch (Exception e) { diff --git a/app/src/main/java/awais/instagrabber/models/Comment.kt b/app/src/main/java/awais/instagrabber/models/Comment.kt index c32e9203..20c03f76 100644 --- a/app/src/main/java/awais/instagrabber/models/Comment.kt +++ b/app/src/main/java/awais/instagrabber/models/Comment.kt @@ -6,25 +6,24 @@ import java.io.Serializable import java.util.* class Comment( - val id: String, + val pk: String, val text: String, - val timestamp: Long, - var likes: Long, - private var liked: Boolean, + val createdAt: Long, + var commentLikeCount: Long, + private var hasLikedComment: Boolean, val user: User, - val replyCount: Int, - val isChild: Boolean, + val childCommentCount: Int ) : Serializable, Cloneable { val dateTime: String - get() = TextUtils.epochSecondToString(timestamp) + get() = TextUtils.epochSecondToString(createdAt) fun getLiked(): Boolean { - return liked + return hasLikedComment } fun setLiked(liked: Boolean) { - likes = if (liked) likes + 1 else likes - 1 - this.liked = liked + commentLikeCount = if (hasLikedComment) commentLikeCount + 1 else commentLikeCount - 1 + this.hasLikedComment = hasLikedComment } @Throws(CloneNotSupportedException::class) @@ -39,31 +38,29 @@ class Comment( other as Comment - if (id != other.id) return false + if (pk != other.pk) return false if (text != other.text) return false - if (timestamp != other.timestamp) return false - if (likes != other.likes) return false - if (liked != other.liked) return false + if (createdAt != other.createdAt) return false + if (commentLikeCount != other.commentLikeCount) return false + if (hasLikedComment != other.hasLikedComment) return false if (user != other.user) return false - if (replyCount != other.replyCount) return false - if (isChild != other.isChild) return false + if (childCommentCount != other.childCommentCount) return false return true } override fun hashCode(): Int { - var result = id.hashCode() + var result = pk.hashCode() result = 31 * result + text.hashCode() - result = 31 * result + timestamp.hashCode() - result = 31 * result + likes.hashCode() - result = 31 * result + liked.hashCode() + result = 31 * result + createdAt.hashCode() + result = 31 * result + commentLikeCount.hashCode() + result = 31 * result + hasLikedComment.hashCode() result = 31 * result + user.hashCode() - result = 31 * result + replyCount - result = 31 * result + isChild.hashCode() + result = 31 * result + childCommentCount return result } override fun toString(): String { - return "Comment(id='$id', text='$text', timestamp=$timestamp, likes=$likes, liked=$liked, user=$user, replyCount=$replyCount, isChild=$isChild)" + return "Comment(pk='$pk', text='$text', createdAt=$createdAt, commentLikeCount=$commentLikeCount, hasLikedComment=$hasLikedComment, user=$user, childCommentCount=$childCommentCount)" } } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/repositories/CommentRepository.java b/app/src/main/java/awais/instagrabber/repositories/CommentRepository.java new file mode 100644 index 00000000..38c64b3a --- /dev/null +++ b/app/src/main/java/awais/instagrabber/repositories/CommentRepository.java @@ -0,0 +1,49 @@ +package awais.instagrabber.repositories; + +import java.util.Map; + +import awais.instagrabber.repositories.responses.CommentsFetchResponse; +import awais.instagrabber.repositories.responses.ChildCommentsFetchResponse; +import retrofit2.Call; +import retrofit2.http.FieldMap; +import retrofit2.http.FormUrlEncoded; +import retrofit2.http.GET; +import retrofit2.http.Header; +import retrofit2.http.POST; +import retrofit2.http.Path; +import retrofit2.http.Query; +import retrofit2.http.QueryMap; + +public interface CommentRepository { + @GET("/api/v1/media/{mediaId}/comments/") + Call fetchComments(@Path("mediaId") final String mediaId, + @QueryMap final Map queryMap); + + @GET("/api/v1/media/{mediaId}/comments/{commentId}/inline_child_comments/") + Call fetchChildComments(@Path("mediaId") final String mediaId, + @Path("commentId") final String commentId, + @QueryMap final Map queryMap); + + @FormUrlEncoded + @POST("/api/v1/media/{mediaId}/comment/") + Call comment(@Path("mediaId") final String mediaId, + @FieldMap final Map signedForm); + + @FormUrlEncoded + @POST("/api/v1/media/{mediaId}/comment/bulk_delete/") + Call commentsBulkDelete(@Path("mediaId") final String mediaId, + @FieldMap final Map signedForm); + + @FormUrlEncoded + @POST("/api/v1/media/{commentId}/comment_like/") + Call commentLike(@Path("commentId") final String commentId, + @FieldMap final Map signedForm); + + @FormUrlEncoded + @POST("/api/v1/media/{commentId}/comment_unlike/") + Call commentUnlike(@Path("commentId") final String commentId, + @FieldMap final Map signedForm); + + @GET("/api/v1/language/translate/") + Call translate(@QueryMap final Map form); +} diff --git a/app/src/main/java/awais/instagrabber/repositories/MediaRepository.java b/app/src/main/java/awais/instagrabber/repositories/MediaRepository.java index 6c71040b..f2c78b11 100644 --- a/app/src/main/java/awais/instagrabber/repositories/MediaRepository.java +++ b/app/src/main/java/awais/instagrabber/repositories/MediaRepository.java @@ -28,26 +28,6 @@ public interface MediaRepository { @Path("mediaId") final String mediaId, @FieldMap final Map signedForm); - @FormUrlEncoded - @POST("/api/v1/media/{mediaId}/comment/") - Call comment(@Path("mediaId") final String mediaId, - @FieldMap final Map signedForm); - - @FormUrlEncoded - @POST("/api/v1/media/{mediaId}/comment/bulk_delete/") - Call commentsBulkDelete(@Path("mediaId") final String mediaId, - @FieldMap final Map signedForm); - - @FormUrlEncoded - @POST("/api/v1/media/{commentId}/comment_like/") - Call commentLike(@Path("commentId") final String commentId, - @FieldMap final Map signedForm); - - @FormUrlEncoded - @POST("/api/v1/media/{commentId}/comment_unlike/") - Call commentUnlike(@Path("commentId") final String commentId, - @FieldMap final Map signedForm); - @FormUrlEncoded @POST("/api/v1/media/{mediaId}/edit_media/") Call editCaption(@Path("mediaId") final String mediaId, diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/ChildCommentsFetchResponse.java b/app/src/main/java/awais/instagrabber/repositories/responses/ChildCommentsFetchResponse.java new file mode 100644 index 00000000..32a6134c --- /dev/null +++ b/app/src/main/java/awais/instagrabber/repositories/responses/ChildCommentsFetchResponse.java @@ -0,0 +1,47 @@ +package awais.instagrabber.repositories.responses; + +import androidx.annotation.NonNull; + +import java.util.List; + +import awais.instagrabber.models.Comment; + +public class ChildCommentsFetchResponse { + private final int childCommentCount; + private final String nextMinId; + private final List childComments; + + public ChildCommentsFetchResponse(final int childCommentCount, + final String nextMinId, // unconfirmed + final List childComments) { + this.childCommentCount = childCommentCount; + this.nextMinId = nextMinId; + this.childComments = childComments; + } + + public int getChildCommentCount() { + return childCommentCount; + } + + public String getNextMinId() { + return nextMinId; + } + + public boolean hasNext() { + return nextMinId != null; + } + + public List getChildComments() { + return childComments; + } + + @NonNull + @Override + public String toString() { + return "CommentsFetchResponse{" + + "childCommentCount=" + childCommentCount + + ", nextMinId='" + nextMinId + '\'' + + ", childComments=" + childComments + + '}'; + } +} diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/CommentsFetchResponse.java b/app/src/main/java/awais/instagrabber/repositories/responses/CommentsFetchResponse.java new file mode 100644 index 00000000..d2834e45 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/repositories/responses/CommentsFetchResponse.java @@ -0,0 +1,47 @@ +package awais.instagrabber.repositories.responses; + +import androidx.annotation.NonNull; + +import java.util.List; + +import awais.instagrabber.models.Comment; + +public class CommentsFetchResponse { + private final int commentCount; + private final String nextMinId; + private final List comments; + + public CommentsFetchResponse(final int commentCount, + final String nextMinId, + final List comments) { + this.commentCount = commentCount; + this.nextMinId = nextMinId; + this.comments = comments; + } + + public int getCommentCount() { + return commentCount; + } + + public String getNextMinId() { + return nextMinId; + } + + public boolean hasNext() { + return nextMinId != null; + } + + public List getComments() { + return comments; + } + + @NonNull + @Override + public String toString() { + return "CommentsFetchResponse{" + + "commentCount=" + commentCount + + ", nextMinId='" + nextMinId + '\'' + + ", comments=" + comments + + '}'; + } +} diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/GraphQLCommentsFetchResponse.java b/app/src/main/java/awais/instagrabber/repositories/responses/GraphQLCommentsFetchResponse.java deleted file mode 100644 index 22c3c7b9..00000000 --- a/app/src/main/java/awais/instagrabber/repositories/responses/GraphQLCommentsFetchResponse.java +++ /dev/null @@ -1,51 +0,0 @@ -package awais.instagrabber.repositories.responses; - -import androidx.annotation.NonNull; - -import java.util.List; - -import awais.instagrabber.models.Comment; - -public class GraphQLCommentsFetchResponse { - private final int count; - private final String cursor; - private final boolean hasNext; - private final List comments; - - public GraphQLCommentsFetchResponse(final int count, - final String cursor, - final boolean hasNext, - final List comments) { - this.count = count; - this.cursor = cursor; - this.hasNext = hasNext; - this.comments = comments; - } - - public int getCount() { - return count; - } - - public String getCursor() { - return cursor; - } - - public boolean hasNext() { - return hasNext; - } - - public List getComments() { - return comments; - } - - @NonNull - @Override - public String toString() { - return "GraphQLCommentsFetchResponse{" + - "count=" + count + - ", cursor='" + cursor + '\'' + - ", hasNext=" + hasNext + - ", comments=" + comments + - '}'; - } -} diff --git a/app/src/main/java/awais/instagrabber/viewmodels/CommentsViewerViewModel.java b/app/src/main/java/awais/instagrabber/viewmodels/CommentsViewerViewModel.java index ab42e84d..a7e3c52c 100644 --- a/app/src/main/java/awais/instagrabber/viewmodels/CommentsViewerViewModel.java +++ b/app/src/main/java/awais/instagrabber/viewmodels/CommentsViewerViewModel.java @@ -25,14 +25,14 @@ import java.util.stream.IntStream; import awais.instagrabber.R; import awais.instagrabber.models.Comment; import awais.instagrabber.models.Resource; -import awais.instagrabber.repositories.responses.FriendshipStatus; -import awais.instagrabber.repositories.responses.GraphQLCommentsFetchResponse; +import awais.instagrabber.repositories.responses.ChildCommentsFetchResponse; +import awais.instagrabber.repositories.responses.CommentsFetchResponse; import awais.instagrabber.repositories.responses.User; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.Utils; +import awais.instagrabber.webservices.CommentService; import awais.instagrabber.webservices.GraphQLService; -import awais.instagrabber.webservices.MediaService; import awais.instagrabber.webservices.ServiceCallback; import retrofit2.Call; import retrofit2.Callback; @@ -48,7 +48,7 @@ public class CommentsViewerViewModel extends ViewModel { private final MutableLiveData>> rootList = new MutableLiveData<>(); private final MutableLiveData rootCount = new MutableLiveData<>(0); private final MutableLiveData>> replyList = new MutableLiveData<>(); - private final GraphQLService service; + private final GraphQLService graphQLService; private String shortCode; private String postId; @@ -57,18 +57,68 @@ public class CommentsViewerViewModel extends ViewModel { private Comment repliesParent; private String repliesCursor; private boolean repliesHasNext = true; - private final MediaService mediaService; + private final CommentService commentService; private List prevReplies; private String prevRepliesCursor; private boolean prevRepliesHasNext = true; + private final ServiceCallback ccb = new ServiceCallback() { + @Override + public void onSuccess(final CommentsFetchResponse result) { + // Log.d(TAG, "onSuccess: " + result); + List comments = result.getComments(); + if (rootCursor == null) { + rootCount.postValue(result.getCommentCount()); + } + if (rootCursor != null) { + comments = mergeList(rootList, comments); + } + rootCursor = result.getNextMinId(); + rootHasNext = result.hasNext(); + rootList.postValue(Resource.success(comments)); + } + + @Override + public void onFailure(final Throwable t) { + Log.e(TAG, "onFailure: ", t); + rootList.postValue(Resource.error(t.getMessage(), getPrevList(rootList))); + } + }; + private final ServiceCallback rcb = new ServiceCallback() { + @Override + public void onSuccess(final ChildCommentsFetchResponse result) { + // Log.d(TAG, "onSuccess: " + result); + List comments = result.getChildComments(); + // Replies + if (repliesCursor == null) { + // add parent to top of replies + comments = ImmutableList.builder() + .add(repliesParent) + .addAll(comments) + .build(); + } + if (repliesCursor != null) { + comments = mergeList(replyList, comments); + } + repliesCursor = result.getNextMinId(); + repliesHasNext = result.hasNext(); + replyList.postValue(Resource.success(comments)); + } + + @Override + public void onFailure(final Throwable t) { + Log.e(TAG, "onFailure: ", t); + replyList.postValue(Resource.error(t.getMessage(), getPrevList(replyList))); + } + }; + public CommentsViewerViewModel() { - service = GraphQLService.getInstance(); + graphQLService = GraphQLService.getInstance(); final String cookie = settingsHelper.getString(Constants.COOKIE); final String deviceUuid = Utils.settingsHelper.getString(Constants.DEVICE_UUID); final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie); final long userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie); - mediaService = MediaService.getInstance(deviceUuid, csrfToken, userIdFromCookie); + commentService = CommentService.getInstance(deviceUuid, csrfToken, userIdFromCookie); } public void setCurrentUser(final User currentUser) { @@ -108,87 +158,45 @@ public class CommentsViewerViewModel extends ViewModel { } public void fetchComments() { - if (shortCode == null) return; - fetchComments(shortCode, true); + if (shortCode == null || postId == null) return; + if (!rootHasNext) return; + rootList.postValue(Resource.loading(getPrevList(rootList))); + if (isLoggedIn.getValue()) { + commentService.fetchComments(postId, rootCursor, ccb); + return; + } + final Call request = graphQLService.fetchComments(shortCode, true, rootCursor); + enqueueRequest(request, true, shortCode, ccb); } public void fetchReplies() { if (repliesParent == null) return; - fetchReplies(repliesParent.getId()); + fetchReplies(repliesParent.getPk()); } public void fetchReplies(@NonNull final String commentId) { - fetchComments(commentId, false); - } - - public void fetchComments(@NonNull final String shortCodeOrCommentId, - final boolean root) { - if (root) { - if (!rootHasNext) return; - rootList.postValue(Resource.loading(getPrevList(rootList))); + if (!repliesHasNext) return; + final List list; + if (repliesParent != null && !Objects.equals(repliesParent.getPk(), commentId)) { + repliesCursor = null; + repliesHasNext = false; + list = Collections.emptyList(); } else { - if (!repliesHasNext) return; - final List list; - if (repliesParent != null && !Objects.equals(repliesParent.getId(), shortCodeOrCommentId)) { - repliesCursor = null; - repliesHasNext = false; - list = Collections.emptyList(); - } else { - list = getPrevList(replyList); - } - replyList.postValue(Resource.loading(list)); + list = getPrevList(replyList); } - final Call request = service.fetchComments(shortCodeOrCommentId, root, root ? rootCursor : repliesCursor); - enqueueRequest(request, root, shortCodeOrCommentId, new ServiceCallback() { - @Override - public void onSuccess(final GraphQLCommentsFetchResponse result) { - // Log.d(TAG, "onSuccess: " + result); - List comments = result.getComments(); - if (root) { - if (rootCursor == null) { - rootCount.postValue(result.getCount()); - } - if (rootCursor != null) { - comments = mergeList(rootList, comments); - } - rootCursor = result.getCursor(); - rootHasNext = result.hasNext(); - rootList.postValue(Resource.success(comments)); - return; - } - // Replies - if (repliesCursor == null) { - // add parent to top of replies - comments = ImmutableList.builder() - .add(repliesParent) - .addAll(comments) - .build(); - } - if (repliesCursor != null) { - comments = mergeList(replyList, comments); - } - repliesCursor = result.getCursor(); - repliesHasNext = result.hasNext(); - replyList.postValue(Resource.success(comments)); - - } - - @Override - public void onFailure(final Throwable t) { - Log.e(TAG, "onFailure: ", t); - if (root) { - rootList.postValue(Resource.error(t.getMessage(), getPrevList(rootList))); - return; - } - replyList.postValue(Resource.error(t.getMessage(), getPrevList(replyList))); - } - }); + replyList.postValue(Resource.loading(list)); + if (isLoggedIn.getValue()) { + commentService.fetchChildComments(postId, commentId, rootCursor, rcb); + return; + } + final Call request = graphQLService.fetchComments(commentId, false, repliesCursor); + enqueueRequest(request, false, commentId, rcb); } private void enqueueRequest(@NonNull final Call request, final boolean root, final String shortCodeOrCommentId, - final ServiceCallback callback) { + final ServiceCallback callback) { request.enqueue(new Callback() { @Override public void onResponse(@NonNull final Call call, @NonNull final Response response) { @@ -208,14 +216,16 @@ public class CommentsViewerViewModel extends ViewModel { final int count = body.optInt("count"); final JSONObject pageInfo = body.getJSONObject("page_info"); final boolean hasNextPage = pageInfo.getBoolean("has_next_page"); - final String endCursor = pageInfo.isNull("end_cursor") ? null : pageInfo.optString("end_cursor"); + final String endCursor = pageInfo.isNull("end_cursor") || !hasNextPage ? null : pageInfo.optString("end_cursor"); final JSONArray commentsJsonArray = body.getJSONArray("edges"); final ImmutableList.Builder builder = ImmutableList.builder(); for (int i = 0; i < commentsJsonArray.length(); i++) { final Comment commentModel = getComment(commentsJsonArray.getJSONObject(i).getJSONObject("node"), root); builder.add(commentModel); } - callback.onSuccess(new GraphQLCommentsFetchResponse(count, endCursor, hasNextPage, builder.build())); + callback.onSuccess(root ? + new CommentsFetchResponse(count, endCursor, builder.build()) : + new ChildCommentsFetchResponse(count, endCursor, builder.build())); } catch (Exception e) { Log.e(TAG, "onResponse", e); callback.onFailure(e); @@ -252,8 +262,7 @@ public class CommentsViewerViewModel extends ViewModel { likedBy != null ? likedBy.optLong("count", 0) : 0, commentJsonObject.getBoolean("viewer_has_liked"), user, - replyCount, - !root); + replyCount); } @NonNull @@ -278,12 +287,12 @@ public class CommentsViewerViewModel extends ViewModel { public void showReplies(final Comment comment) { if (comment == null) return; - if (repliesParent == null || !Objects.equals(repliesParent.getId(), comment.getId())) { + if (repliesParent == null || !Objects.equals(repliesParent.getPk(), comment.getPk())) { repliesParent = comment; prevReplies = null; prevRepliesCursor = null; prevRepliesHasNext = true; - fetchReplies(comment.getId()); + fetchReplies(comment.getPk()); return; } if (prevReplies != null && !prevReplies.isEmpty()) { @@ -296,7 +305,7 @@ public class CommentsViewerViewModel extends ViewModel { // prev list was null or empty, fetch prevRepliesCursor = null; prevRepliesHasNext = true; - fetchReplies(comment.getId()); + fetchReplies(comment.getPk()); } public LiveData> likeComment(@NonNull final Comment comment, final boolean liked, final boolean isReply) { @@ -319,9 +328,9 @@ public class CommentsViewerViewModel extends ViewModel { } }; if (liked) { - mediaService.commentLike(comment.getId(), callback); + commentService.commentLike(comment.getPk(), callback); } else { - mediaService.commentUnlike(comment.getId(), callback); + commentService.commentUnlike(comment.getPk(), callback); } return data; } @@ -333,7 +342,7 @@ public class CommentsViewerViewModel extends ViewModel { if (list == null) return; final List copy = new ArrayList<>(list); OptionalInt indexOpt = IntStream.range(0, copy.size()) - .filter(i -> copy.get(i) != null && Objects.equals(copy.get(i).getId(), comment.getId())) + .filter(i -> copy.get(i) != null && Objects.equals(copy.get(i).getPk(), comment.getPk())) .findFirst(); if (!indexOpt.isPresent()) return; try { @@ -352,13 +361,13 @@ public class CommentsViewerViewModel extends ViewModel { final MutableLiveData> data = new MutableLiveData<>(Resource.loading(null)); String replyToId = null; if (isReply && repliesParent != null) { - replyToId = repliesParent.getId(); + replyToId = repliesParent.getPk(); } if (isReply && replyToId == null) { data.postValue(Resource.error(null, null)); return data; } - mediaService.comment(postId, text, replyToId, new ServiceCallback() { + commentService.comment(postId, text, replyToId, new ServiceCallback() { @Override public void onSuccess(final Comment result) { if (result == null) { @@ -396,12 +405,12 @@ public class CommentsViewerViewModel extends ViewModel { public void translate(@NonNull final Comment comment, @NonNull final ServiceCallback callback) { - mediaService.translate(comment.getId(), "2", callback); + commentService.translate(comment.getPk(), callback); } public LiveData> deleteComment(@NonNull final Comment comment, final boolean isReply) { final MutableLiveData> data = new MutableLiveData<>(Resource.loading(null)); - mediaService.deleteComment(postId, comment.getId(), new ServiceCallback() { + commentService.deleteComment(postId, comment.getPk(), new ServiceCallback() { @Override public void onSuccess(final Boolean result) { if (result == null || !result) { @@ -425,7 +434,7 @@ public class CommentsViewerViewModel extends ViewModel { final List list = getPrevList(isReply ? replyList : rootList); final List updated = list.stream() .filter(Objects::nonNull) - .filter(c -> !Objects.equals(c.getId(), comment.getId())) + .filter(c -> !Objects.equals(c.getPk(), comment.getPk())) .collect(Collectors.toList()); final MutableLiveData>> liveData = isReply ? replyList : rootList; liveData.postValue(Resource.success(updated)); diff --git a/app/src/main/java/awais/instagrabber/webservices/CommentService.java b/app/src/main/java/awais/instagrabber/webservices/CommentService.java new file mode 100644 index 00000000..2a0b72ea --- /dev/null +++ b/app/src/main/java/awais/instagrabber/webservices/CommentService.java @@ -0,0 +1,324 @@ +package awais.instagrabber.webservices; + +import android.util.Log; + +import androidx.annotation.NonNull; + +import com.google.gson.Gson; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; + +import awais.instagrabber.models.Comment; +import awais.instagrabber.repositories.CommentRepository; +import awais.instagrabber.repositories.responses.ChildCommentsFetchResponse; +import awais.instagrabber.repositories.responses.CommentsFetchResponse; +import awais.instagrabber.repositories.responses.User; +import awais.instagrabber.utils.TextUtils; +import awais.instagrabber.utils.Utils; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +public class CommentService extends BaseService { + private static final String TAG = "CommentService"; + + private final CommentRepository repository; + private final String deviceUuid, csrfToken; + private final long userId; + + private static CommentService instance; + + private CommentService(final String deviceUuid, + final String csrfToken, + final long userId) { + this.deviceUuid = deviceUuid; + this.csrfToken = csrfToken; + this.userId = userId; + repository = RetrofitFactory.INSTANCE + .getRetrofit() + .create(CommentRepository.class); + } + + public String getCsrfToken() { + return csrfToken; + } + + public String getDeviceUuid() { + return deviceUuid; + } + + public long getUserId() { + return userId; + } + + public static CommentService getInstance(final String deviceUuid, final String csrfToken, final long userId) { + if (instance == null + || !Objects.equals(instance.getCsrfToken(), csrfToken) + || !Objects.equals(instance.getDeviceUuid(), deviceUuid) + || !Objects.equals(instance.getUserId(), userId)) { + instance = new CommentService(deviceUuid, csrfToken, userId); + } + return instance; + } + + public void fetchComments(@NonNull final String mediaId, + final String maxId, + @NonNull final ServiceCallback callback) { + final Map form = new HashMap<>(); + form.put("can_support_threading", "true"); + if (maxId != null) form.put("max_id", maxId); + final Call request = repository.fetchComments(mediaId, form); + request.enqueue(new Callback() { + @Override + public void onResponse(@NonNull final Call call, @NonNull final Response response) { + if (callback == null) return; + final CommentsFetchResponse cfr = response.body(); + if (cfr == null) callback.onFailure(new Exception("response is empty")); + callback.onSuccess(cfr); + } + + @Override + public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { + callback.onFailure(t); + } + }); + } + + public void fetchChildComments(@NonNull final String mediaId, + @NonNull final String commentId, + final String maxId, + @NonNull final ServiceCallback callback) { + final Map form = new HashMap<>(); + if (maxId != null) form.put("max_id", maxId); + final Call request = repository.fetchChildComments(mediaId, commentId, form); + request.enqueue(new Callback() { + @Override + public void onResponse(@NonNull final Call call, @NonNull final Response response) { + if (callback == null) return; + final ChildCommentsFetchResponse cfr = response.body(); + if (cfr == null) callback.onFailure(new Exception("response is empty")); + callback.onSuccess(cfr); + } + + @Override + public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { + callback.onFailure(t); + } + }); + } + + public void comment(@NonNull final String mediaId, + @NonNull final String comment, + final String replyToCommentId, + @NonNull final ServiceCallback callback) { + final String module = "self_comments_v2"; + final Map form = new HashMap<>(); + // form.put("user_breadcrumb", userBreadcrumb(comment.length())); + form.put("idempotence_token", UUID.randomUUID().toString()); + form.put("_csrftoken", csrfToken); + form.put("_uid", userId); + form.put("_uuid", deviceUuid); + form.put("comment_text", comment); + form.put("containermodule", module); + if (!TextUtils.isEmpty(replyToCommentId)) { + form.put("replied_to_comment_id", replyToCommentId); + } + final Map signedForm = Utils.sign(form); + final Call commentRequest = repository.comment(mediaId, signedForm); + commentRequest.enqueue(new Callback() { + @Override + public void onResponse(@NonNull final Call call, @NonNull final Response response) { + final String body = response.body(); + if (body == null) { + Log.e(TAG, "Error occurred while creating comment"); + callback.onSuccess(null); + return; + } + try { + final JSONObject jsonObject = new JSONObject(body); + // final String status = jsonObject.optString("status"); + final JSONObject commentJsonObject = jsonObject.optJSONObject("comment"); + Comment comment = null; + if (commentJsonObject != null) { + final JSONObject userJsonObject = commentJsonObject.optJSONObject("user"); + if (userJsonObject != null) { + final Gson gson = new Gson(); + final User user = gson.fromJson(userJsonObject.toString(), User.class); + comment = new Comment( + commentJsonObject.optString("pk"), + commentJsonObject.optString("text"), + commentJsonObject.optLong("created_at"), + 0L, + false, + user, + 0 + ); + } + } + callback.onSuccess(comment); + } catch (Exception e) { + callback.onFailure(e); + } + } + + @Override + public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { + callback.onFailure(t); + } + }); + } + + public void deleteComment(final String mediaId, + final String commentId, + @NonNull final ServiceCallback callback) { + deleteComments(mediaId, Collections.singletonList(commentId), callback); + } + + public void deleteComments(final String mediaId, + final List commentIds, + @NonNull final ServiceCallback callback) { + final Map form = new HashMap<>(); + form.put("comment_ids_to_delete", android.text.TextUtils.join(",", commentIds)); + form.put("_csrftoken", csrfToken); + form.put("_uid", userId); + form.put("_uuid", deviceUuid); + final Map signedForm = Utils.sign(form); + final Call bulkDeleteRequest = repository.commentsBulkDelete(mediaId, signedForm); + bulkDeleteRequest.enqueue(new Callback() { + @Override + public void onResponse(@NonNull final Call call, @NonNull final Response response) { + final String body = response.body(); + if (body == null) { + Log.e(TAG, "Error occurred while deleting comments"); + callback.onSuccess(false); + return; + } + try { + final JSONObject jsonObject = new JSONObject(body); + final String status = jsonObject.optString("status"); + callback.onSuccess(status.equals("ok")); + } catch (JSONException e) { + // Log.e(TAG, "Error parsing body", e); + callback.onFailure(e); + } + } + + @Override + public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { + // Log.e(TAG, "Error deleting comments", t); + callback.onFailure(t); + } + }); + } + + public void commentLike(@NonNull final String commentId, + @NonNull final ServiceCallback callback) { + final Map form = new HashMap<>(); + form.put("_csrftoken", csrfToken); + // form.put("_uid", userId); + // form.put("_uuid", deviceUuid); + final Map signedForm = Utils.sign(form); + final Call commentLikeRequest = repository.commentLike(commentId, signedForm); + commentLikeRequest.enqueue(new Callback() { + @Override + public void onResponse(@NonNull final Call call, @NonNull final Response response) { + final String body = response.body(); + if (body == null) { + Log.e(TAG, "Error occurred while liking comment"); + callback.onSuccess(false); + return; + } + try { + final JSONObject jsonObject = new JSONObject(body); + final String status = jsonObject.optString("status"); + callback.onSuccess(status.equals("ok")); + } catch (JSONException e) { + // Log.e(TAG, "Error parsing body", e); + callback.onFailure(e); + } + } + + @Override + public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { + Log.e(TAG, "Error liking comment", t); + callback.onFailure(t); + } + }); + } + + public void commentUnlike(final String commentId, + @NonNull final ServiceCallback callback) { + final Map form = new HashMap<>(); + form.put("_csrftoken", csrfToken); + // form.put("_uid", userId); + // form.put("_uuid", deviceUuid); + final Map signedForm = Utils.sign(form); + final Call commentUnlikeRequest = repository.commentUnlike(commentId, signedForm); + commentUnlikeRequest.enqueue(new Callback() { + @Override + public void onResponse(@NonNull final Call call, @NonNull final Response response) { + final String body = response.body(); + if (body == null) { + Log.e(TAG, "Error occurred while unliking comment"); + callback.onSuccess(false); + return; + } + try { + final JSONObject jsonObject = new JSONObject(body); + final String status = jsonObject.optString("status"); + callback.onSuccess(status.equals("ok")); + } catch (JSONException e) { + // Log.e(TAG, "Error parsing body", e); + callback.onFailure(e); + } + } + + @Override + public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { + Log.e(TAG, "Error unliking comment", t); + callback.onFailure(t); + } + }); + } + + public void translate(final String id, + @NonNull final ServiceCallback callback) { + final Map form = new HashMap<>(); + form.put("id", String.valueOf(id)); + form.put("type", "2"); + final Call request = repository.translate(form); + request.enqueue(new Callback() { + @Override + public void onResponse(@NonNull final Call call, @NonNull final Response response) { + final String body = response.body(); + if (body == null) { + Log.e(TAG, "Error occurred while translating"); + callback.onSuccess(null); + return; + } + try { + final JSONObject jsonObject = new JSONObject(body); + final String translation = jsonObject.optString("translation"); + callback.onSuccess(translation); + } catch (JSONException e) { + // Log.e(TAG, "Error parsing body", e); + callback.onFailure(e); + } + } + + @Override + public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { + Log.e(TAG, "Error translating", t); + callback.onFailure(t); + } + }); + } +} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/webservices/MediaService.java b/app/src/main/java/awais/instagrabber/webservices/MediaService.java index 2e9dd604..333a526d 100644 --- a/app/src/main/java/awais/instagrabber/webservices/MediaService.java +++ b/app/src/main/java/awais/instagrabber/webservices/MediaService.java @@ -171,181 +171,6 @@ public class MediaService extends BaseService { }); } - public void comment(@NonNull final String mediaId, - @NonNull final String comment, - final String replyToCommentId, - @NonNull final ServiceCallback callback) { - final String module = "self_comments_v2"; - final Map form = new HashMap<>(); - // form.put("user_breadcrumb", userBreadcrumb(comment.length())); - form.put("idempotence_token", UUID.randomUUID().toString()); - form.put("_csrftoken", csrfToken); - form.put("_uid", userId); - form.put("_uuid", deviceUuid); - form.put("comment_text", comment); - form.put("containermodule", module); - if (!TextUtils.isEmpty(replyToCommentId)) { - form.put("replied_to_comment_id", replyToCommentId); - } - final Map signedForm = Utils.sign(form); - final Call commentRequest = repository.comment(mediaId, signedForm); - commentRequest.enqueue(new Callback() { - @Override - public void onResponse(@NonNull final Call call, @NonNull final Response response) { - final String body = response.body(); - if (body == null) { - Log.e(TAG, "Error occurred while creating comment"); - callback.onSuccess(null); - return; - } - try { - final JSONObject jsonObject = new JSONObject(body); - // final String status = jsonObject.optString("status"); - final JSONObject commentJsonObject = jsonObject.optJSONObject("comment"); - Comment comment = null; - if (commentJsonObject != null) { - final JSONObject userJsonObject = commentJsonObject.optJSONObject("user"); - if (userJsonObject != null) { - final Gson gson = new Gson(); - final User user = gson.fromJson(userJsonObject.toString(), User.class); - comment = new Comment( - commentJsonObject.optString("pk"), - commentJsonObject.optString("text"), - commentJsonObject.optLong("created_at"), - 0, - false, - user, - 0, - !TextUtils.isEmpty(replyToCommentId) - ); - } - } - callback.onSuccess(comment); - } catch (Exception e) { - callback.onFailure(e); - } - } - - @Override - public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { - callback.onFailure(t); - } - }); - } - - public void deleteComment(final String mediaId, - final String commentId, - @NonNull final ServiceCallback callback) { - deleteComments(mediaId, Collections.singletonList(commentId), callback); - } - - public void deleteComments(final String mediaId, - final List commentIds, - @NonNull final ServiceCallback callback) { - final Map form = new HashMap<>(); - form.put("comment_ids_to_delete", android.text.TextUtils.join(",", commentIds)); - form.put("_csrftoken", csrfToken); - form.put("_uid", userId); - form.put("_uuid", deviceUuid); - final Map signedForm = Utils.sign(form); - final Call bulkDeleteRequest = repository.commentsBulkDelete(mediaId, signedForm); - bulkDeleteRequest.enqueue(new Callback() { - @Override - public void onResponse(@NonNull final Call call, @NonNull final Response response) { - final String body = response.body(); - if (body == null) { - Log.e(TAG, "Error occurred while deleting comments"); - callback.onSuccess(false); - return; - } - try { - final JSONObject jsonObject = new JSONObject(body); - final String status = jsonObject.optString("status"); - callback.onSuccess(status.equals("ok")); - } catch (JSONException e) { - // Log.e(TAG, "Error parsing body", e); - callback.onFailure(e); - } - } - - @Override - public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { - // Log.e(TAG, "Error deleting comments", t); - callback.onFailure(t); - } - }); - } - - public void commentLike(@NonNull final String commentId, - @NonNull final ServiceCallback callback) { - final Map form = new HashMap<>(); - form.put("_csrftoken", csrfToken); - // form.put("_uid", userId); - // form.put("_uuid", deviceUuid); - final Map signedForm = Utils.sign(form); - final Call commentLikeRequest = repository.commentLike(commentId, signedForm); - commentLikeRequest.enqueue(new Callback() { - @Override - public void onResponse(@NonNull final Call call, @NonNull final Response response) { - final String body = response.body(); - if (body == null) { - Log.e(TAG, "Error occurred while liking comment"); - callback.onSuccess(false); - return; - } - try { - final JSONObject jsonObject = new JSONObject(body); - final String status = jsonObject.optString("status"); - callback.onSuccess(status.equals("ok")); - } catch (JSONException e) { - // Log.e(TAG, "Error parsing body", e); - callback.onFailure(e); - } - } - - @Override - public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { - Log.e(TAG, "Error liking comment", t); - callback.onFailure(t); - } - }); - } - - public void commentUnlike(final String commentId, - @NonNull final ServiceCallback callback) { - final Map form = new HashMap<>(); - form.put("_csrftoken", csrfToken); - // form.put("_uid", userId); - // form.put("_uuid", deviceUuid); - final Map signedForm = Utils.sign(form); - final Call commentUnlikeRequest = repository.commentUnlike(commentId, signedForm); - commentUnlikeRequest.enqueue(new Callback() { - @Override - public void onResponse(@NonNull final Call call, @NonNull final Response response) { - final String body = response.body(); - if (body == null) { - Log.e(TAG, "Error occurred while unliking comment"); - callback.onSuccess(false); - return; - } - try { - final JSONObject jsonObject = new JSONObject(body); - final String status = jsonObject.optString("status"); - callback.onSuccess(status.equals("ok")); - } catch (JSONException e) { - // Log.e(TAG, "Error parsing body", e); - callback.onFailure(e); - } - } - - @Override - public void onFailure(@NonNull final Call call, @NonNull final Throwable t) { - Log.e(TAG, "Error unliking comment", t); - callback.onFailure(t); - } - }); - } - public void editCaption(final String postId, final String newCaption, @NonNull final ServiceCallback callback) {