1
0
mirror of https://github.com/KokaKiwi/BarInsta synced 2024-11-22 22:57:29 +00:00

comments viewer improvement

1. Viewing parent comments is now paginated, no more long waits
2. Liking a comment will no longer refresh the entire comment list
3. The pink tint on liked comments from v18 is restored

also removed FeedStoriesFetcher which was deprecated by c24fd01
This commit is contained in:
Austin Huang 2020-12-18 15:45:17 -05:00
parent 4157c113f8
commit 3899b9adfa
No known key found for this signature in database
GPG Key ID: 84C23AA04587A91F
8 changed files with 106 additions and 142 deletions

View File

@ -83,8 +83,8 @@ public final class CommentsAdapter extends ListAdapter<CommentModel, RecyclerVie
} }
}; };
private final CommentCallback commentCallback; private final CommentCallback commentCallback;
private CommentModel selected; private CommentModel selected, toChangeLike;
private int selectedIndex; private int selectedIndex, likedIndex;
public CommentsAdapter(final CommentCallback commentCallback) { public CommentsAdapter(final CommentCallback commentCallback) {
super(DIFF_CALLBACK); super(DIFF_CALLBACK);
@ -106,10 +106,12 @@ public final class CommentsAdapter extends ListAdapter<CommentModel, RecyclerVie
@Override @Override
public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position) { public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position) {
final CommentModel commentModel = getItem(position); CommentModel commentModel = getItem(position);
if (commentModel == null) return; if (commentModel == null) return;
final int type = getItemViewType(position); final int type = getItemViewType(position);
final boolean selected = this.selected != null && this.selected.getId().equals(commentModel.getId()); final boolean selected = this.selected != null && this.selected.getId().equals(commentModel.getId());
final boolean toLike = this.toChangeLike != null && this.toChangeLike.getId().equals(commentModel.getId());
if (toLike) commentModel = this.toChangeLike;
if (type == TYPE_PARENT) { if (type == TYPE_PARENT) {
final ParentCommentViewHolder viewHolder = (ParentCommentViewHolder) holder; final ParentCommentViewHolder viewHolder = (ParentCommentViewHolder) holder;
viewHolder.bind(commentModel, selected, commentCallback); viewHolder.bind(commentModel, selected, commentCallback);
@ -174,6 +176,14 @@ public final class CommentsAdapter extends ListAdapter<CommentModel, RecyclerVie
notifyItemChanged(selectedIndex); notifyItemChanged(selectedIndex);
} }
public void setLiked(final CommentModel commentModel, final boolean liked) {
likedIndex = getCurrentList().indexOf(commentModel);
CommentModel newCommentModel = commentModel;
newCommentModel.setLiked(liked);
this.toChangeLike = newCommentModel;
notifyItemChanged(likedIndex);
}
public CommentModel getSelected() { public CommentModel getSelected() {
return selected; return selected;
} }

View File

@ -89,8 +89,7 @@ public final class ChildCommentViewHolder extends RecyclerView.ViewHolder {
public final void setLiked(final boolean liked) { public final void setLiked(final boolean liked) {
if (liked) { if (liked) {
// container.setBackgroundColor(0x40FF69B4); itemView.setBackgroundColor(itemView.getResources().getColor(R.color.comment_liked));
return;
} }
} }
} }

View File

@ -89,8 +89,7 @@ public final class ParentCommentViewHolder extends RecyclerView.ViewHolder {
public final void setLiked(final boolean liked) { public final void setLiked(final boolean liked) {
if (liked) { if (liked) {
// container.setBackgroundColor(0x40FF69B4); itemView.setBackgroundColor(itemView.getResources().getColor(R.color.comment_liked));
return;
} }
} }
} }

View File

@ -28,11 +28,12 @@ import static awais.instagrabber.utils.Utils.logCollector;
public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentModel>> { public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentModel>> {
private static final String TAG = "CommentsFetcher"; private static final String TAG = "CommentsFetcher";
private final String shortCode; private final String shortCode, endCursor;
private final FetchListener<List<CommentModel>> fetchListener; private final FetchListener<List<CommentModel>> fetchListener;
public CommentsFetcher(final String shortCode, final FetchListener<List<CommentModel>> fetchListener) { public CommentsFetcher(final String shortCode, final String endCursor, final FetchListener<List<CommentModel>> fetchListener) {
this.shortCode = shortCode; this.shortCode = shortCode;
this.endCursor = endCursor;
this.fetchListener = fetchListener; this.fetchListener = fetchListener;
} }
@ -48,6 +49,7 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
https://www.instagram.com/graphql/query/?query_hash=51fdd02b67508306ad4484ff574a0b62&variables={"comment_id":"18100041898085322","first":50,"after":""} https://www.instagram.com/graphql/query/?query_hash=51fdd02b67508306ad4484ff574a0b62&variables={"comment_id":"18100041898085322","first":50,"after":""}
*/ */
final List<CommentModel> commentModels = getParentComments(); final List<CommentModel> commentModels = getParentComments();
if (commentModels != null) {
for (final CommentModel commentModel : commentModels) { for (final CommentModel commentModel : commentModels) {
final List<CommentModel> childCommentModels = commentModel.getChildCommentModels(); final List<CommentModel> childCommentModels = commentModel.getChildCommentModels();
if (childCommentModels != null) { if (childCommentModels != null) {
@ -60,6 +62,7 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
} }
} }
} }
}
return commentModels; return commentModels;
} }
@ -76,11 +79,10 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
@NonNull @NonNull
private synchronized List<CommentModel> getChildComments(final String commentId) { private synchronized List<CommentModel> getChildComments(final String commentId) {
final List<CommentModel> commentModels = new ArrayList<>(); final List<CommentModel> commentModels = new ArrayList<>();
String endCursor = ""; String childEndCursor = "";
while (endCursor != null) { while (childEndCursor != null) {
final String url = "https://www.instagram.com/graphql/query/?query_hash=51fdd02b67508306ad4484ff574a0b62&variables=" + final String url = "https://www.instagram.com/graphql/query/?query_hash=51fdd02b67508306ad4484ff574a0b62&variables=" +
"{\"comment_id\":\"" + commentId + "\",\"first\":50,\"after\":\"" + endCursor + "\"}"; "{\"comment_id\":\"" + commentId + "\",\"first\":50,\"after\":\"" + childEndCursor + "\"}";
try { try {
final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setUseCaches(false); conn.setUseCaches(false);
@ -93,8 +95,8 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
.getJSONObject("edge_threaded_comments"); .getJSONObject("edge_threaded_comments");
final JSONObject pageInfo = data.getJSONObject("page_info"); final JSONObject pageInfo = data.getJSONObject("page_info");
endCursor = pageInfo.getString("end_cursor"); childEndCursor = pageInfo.getString("end_cursor");
if (TextUtils.isEmpty(endCursor)) endCursor = null; if (TextUtils.isEmpty(childEndCursor)) childEndCursor = null;
final JSONArray childComments = data.optJSONArray("edges"); final JSONArray childComments = data.optJSONArray("edges");
if (childComments != null) { if (childComments != null) {
@ -152,17 +154,14 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
@NonNull @NonNull
private synchronized List<CommentModel> getParentComments() { private synchronized List<CommentModel> getParentComments() {
final List<CommentModel> commentModels = new ArrayList<>(); final List<CommentModel> commentModels = new ArrayList<>();
String endCursor = "";
while (endCursor != null) {
final String url = "https://www.instagram.com/graphql/query/?query_hash=bc3296d1ce80a24b1b6e40b1e72903f5&variables=" + final String url = "https://www.instagram.com/graphql/query/?query_hash=bc3296d1ce80a24b1b6e40b1e72903f5&variables=" +
"{\"shortcode\":\"" + shortCode + "\",\"first\":50,\"after\":\"" + endCursor + "\"}"; "{\"shortcode\":\"" + shortCode + "\",\"first\":50,\"after\":\"" + endCursor.replace("\"", "\\\"") + "\"}";
try { try {
final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setUseCaches(false); conn.setUseCaches(false);
conn.connect(); conn.connect();
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) break; if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) return null;
else { else {
final JSONObject parentComments = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("data") final JSONObject parentComments = new JSONObject(NetworkUtils.readFromConnection(conn)).getJSONObject("data")
.getJSONObject("shortcode_media") .getJSONObject("shortcode_media")
@ -170,8 +169,7 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
"edge_media_to_parent_comment"); "edge_media_to_parent_comment");
final JSONObject pageInfo = parentComments.getJSONObject("page_info"); final JSONObject pageInfo = parentComments.getJSONObject("page_info");
endCursor = pageInfo.optString("end_cursor"); final String foundEndCursor = pageInfo.optString("end_cursor");
if (TextUtils.isEmpty(endCursor)) endCursor = null;
// final boolean containsToken = endCursor.contains("bifilter_token"); // final boolean containsToken = endCursor.contains("bifilter_token");
// if (!Utils.isEmpty(endCursor) && (containsToken || endCursor.contains("cached_comments_cursor"))) { // if (!Utils.isEmpty(endCursor) && (containsToken || endCursor.contains("cached_comments_cursor"))) {
@ -219,6 +217,7 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
likedBy != null ? likedBy.optLong("count", 0) : 0, likedBy != null ? likedBy.optLong("count", 0) : 0,
comment.getBoolean("viewer_has_liked"), comment.getBoolean("viewer_has_liked"),
profileModel); profileModel);
commentModel.setPageCursor(!TextUtils.isEmpty(foundEndCursor), TextUtils.isEmpty(foundEndCursor) ? null : foundEndCursor);
JSONObject tempJsonObject; JSONObject tempJsonObject;
final JSONArray childCommentsArray; final JSONArray childCommentsArray;
final int childCommentsLen; final int childCommentsLen;
@ -280,8 +279,7 @@ public final class CommentsFetcher extends AsyncTask<Void, Void, List<CommentMod
logCollector.appendException(e, LogCollector.LogFile.ASYNC_COMMENTS_FETCHER, "getParentComments", logCollector.appendException(e, LogCollector.LogFile.ASYNC_COMMENTS_FETCHER, "getParentComments",
new Pair<>("commentModelsList.size", commentModels.size())); new Pair<>("commentModelsList.size", commentModels.size()));
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
break; return null;
}
} }
return commentModels; return commentModels;
} }

View File

@ -1,93 +0,0 @@
package awais.instagrabber.asyncs;
import android.os.AsyncTask;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONObject;
import java.net.HttpURLConnection;
import java.net.URL;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.FeedStoryModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.NetworkUtils;
import awaisomereport.LogCollector.LogFile;
import static awais.instagrabber.utils.Utils.logCollector;
public final class FeedStoriesFetcher extends AsyncTask<Void, Void, FeedStoryModel[]> {
private final FetchListener<FeedStoryModel[]> fetchListener;
public FeedStoriesFetcher(final FetchListener<FeedStoryModel[]> fetchListener) {
this.fetchListener = fetchListener;
}
@Override
protected FeedStoryModel[] doInBackground(final Void... voids) {
FeedStoryModel[] result = null;
String url = "https://www.instagram.com/graphql/query/?query_hash=b7b84d884400bc5aa7cfe12ae843a091&variables=" +
"{\"only_stories\":true,\"stories_prefetch\":false,\"stories_video_dash_manifest\":false}";
try {
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setInstanceFollowRedirects(false);
conn.setUseCaches(false);
conn.connect();
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
final JSONArray feedStoriesReel = new JSONObject(NetworkUtils.readFromConnection(conn))
.getJSONObject("data")
.getJSONObject(Constants.EXTRAS_USER)
.getJSONObject("feed_reels_tray")
.getJSONObject("edge_reels_tray_to_reel")
.getJSONArray("edges");
conn.disconnect();
final int storiesLen = feedStoriesReel.length();
final FeedStoryModel[] feedStoryModels = new FeedStoryModel[storiesLen];
final String[] feedStoryIDs = new String[storiesLen];
for (int i = 0; i < storiesLen; ++i) {
final JSONObject node = feedStoriesReel.getJSONObject(i).getJSONObject("node");
final JSONObject user = node.getJSONObject(node.has("user") ? "user" : "owner");
final ProfileModel profileModel = new ProfileModel(false, false, false,
user.getString("id"),
user.getString("username"),
null, null, null,
user.getString("profile_pic_url"),
null, 0, 0, 0, false, false, false, false);
final String id = node.getString("id");
final boolean fullyRead = !node.isNull("seen") && node.getLong("seen") == node.getLong("latest_reel_media");
feedStoryIDs[i] = id;
feedStoryModels[i] = new FeedStoryModel(id, profileModel, fullyRead);
}
result = feedStoryModels;
}
conn.disconnect();
} catch (final Exception e) {
if (logCollector != null)
logCollector.appendException(e, LogFile.ASYNC_FEED_STORY_FETCHER, "doInBackground");
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e);
}
return result;
}
@Override
protected void onPreExecute() {
if (fetchListener != null) fetchListener.doBefore();
}
@Override
protected void onPostExecute(final FeedStoryModel[] result) {
if (fetchListener != null) fetchListener.onResult(result);
}
}

View File

@ -32,11 +32,18 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment; import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import awais.instagrabber.BuildConfig;
import awais.instagrabber.R; import awais.instagrabber.R;
import awais.instagrabber.adapters.CommentsAdapter; import awais.instagrabber.adapters.CommentsAdapter;
import awais.instagrabber.asyncs.CommentsFetcher; import awais.instagrabber.asyncs.CommentsFetcher;
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
import awais.instagrabber.databinding.FragmentCommentsBinding; import awais.instagrabber.databinding.FragmentCommentsBinding;
import awais.instagrabber.dialogs.ProfilePicDialogFragment; import awais.instagrabber.dialogs.ProfilePicDialogFragment;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.CommentModel; import awais.instagrabber.models.CommentModel;
import awais.instagrabber.models.ProfileModel; import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
@ -56,8 +63,9 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
private CommentsAdapter commentsAdapter; private CommentsAdapter commentsAdapter;
private FragmentCommentsBinding binding; private FragmentCommentsBinding binding;
private String shortCode; private LinearLayoutManager layoutManager;
private String userId; private RecyclerLazyLoader lazyLoader;
private String shortCode, userId, endCursor = null;
private Resources resources; private Resources resources;
private InputMethodManager imm; private InputMethodManager imm;
private AppCompatActivity fragmentActivity; private AppCompatActivity fragmentActivity;
@ -65,8 +73,30 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
private boolean shouldRefresh = true; private boolean shouldRefresh = true;
private MediaService mediaService; private MediaService mediaService;
private String postId; private String postId;
private AsyncTask<Void, Void, List<CommentModel>> currentlyRunning;
private CommentsViewModel commentsViewModel; private CommentsViewModel commentsViewModel;
private final FetchListener<List<CommentModel>> fetchListener = new FetchListener<List<CommentModel>>() {
@Override
public void doBefore() {
binding.swipeRefreshLayout.setRefreshing(true);
}
@Override
public void onResult(final List<CommentModel> commentModels) {
endCursor = commentModels.get(0).getEndCursor();
if (commentModels != null && commentModels.size() > 0) {
List<CommentModel> list = commentsViewModel.getList().getValue();
list = list != null ? new LinkedList<>(list) : new LinkedList<>();
// final int oldSize = list != null ? list.size() : 0;
list.addAll(commentModels);
commentsViewModel.getList().postValue(list);
}
binding.swipeRefreshLayout.setRefreshing(false);
stopCurrentExecutor();
}
};
private final CommentsAdapter.CommentCallback commentCallback = new CommentsAdapter.CommentCallback() { private final CommentsAdapter.CommentCallback commentCallback = new CommentsAdapter.CommentCallback() {
@Override @Override
public void onClick(final CommentModel comment) { public void onClick(final CommentModel comment) {
@ -181,11 +211,11 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
@Override @Override
public void onRefresh() { public void onRefresh() {
binding.swipeRefreshLayout.setRefreshing(true); endCursor = null;
new CommentsFetcher(shortCode, commentModels -> { lazyLoader.resetState();
commentsViewModel.getList().postValue(commentModels); commentsViewModel.getList().postValue(Collections.emptyList());
binding.swipeRefreshLayout.setRefreshing(false); stopCurrentExecutor();
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); currentlyRunning = new CommentsFetcher(shortCode, "", fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} }
private void init() { private void init() {
@ -198,7 +228,8 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
binding.swipeRefreshLayout.setOnRefreshListener(this); binding.swipeRefreshLayout.setOnRefreshListener(this);
binding.swipeRefreshLayout.setRefreshing(true); binding.swipeRefreshLayout.setRefreshing(true);
commentsViewModel = new ViewModelProvider(this).get(CommentsViewModel.class); commentsViewModel = new ViewModelProvider(this).get(CommentsViewModel.class);
binding.rvComments.setLayoutManager(new LinearLayoutManager(getContext())); layoutManager = new LinearLayoutManager(getContext());
binding.rvComments.setLayoutManager(layoutManager);
commentsAdapter = new CommentsAdapter(commentCallback); commentsAdapter = new CommentsAdapter(commentCallback);
binding.rvComments.setAdapter(commentsAdapter); binding.rvComments.setAdapter(commentsAdapter);
commentsViewModel.getList().observe(getViewLifecycleOwner(), commentsAdapter::submitList); commentsViewModel.getList().observe(getViewLifecycleOwner(), commentsAdapter::submitList);
@ -226,6 +257,13 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
}); });
binding.commentField.setEndIconOnClickListener(newCommentListener); binding.commentField.setEndIconOnClickListener(newCommentListener);
} }
lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> {
if (!TextUtils.isEmpty(endCursor))
currentlyRunning = new CommentsFetcher(shortCode, endCursor, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
endCursor = null;
});
binding.rvComments.addOnScrollListener(lazyLoader);
stopCurrentExecutor();
onRefresh(); onRefresh();
} }
@ -301,8 +339,6 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
Utils.copyText(context, "@" + profileModel.getUsername() + ": " + commentModel.getText()); Utils.copyText(context, "@" + profileModel.getUsername() + ": " + commentModel.getText());
break; break;
case 3: // reply to comment case 3: // reply to comment
// final View focus = binding.rvComments.findViewWithTag(commentModel);
// focus.setBackgroundColor(0x80888888);
commentsAdapter.setSelected(commentModel); commentsAdapter.setSelected(commentModel);
String mention = "@" + profileModel.getUsername() + " "; String mention = "@" + profileModel.getUsername() + " ";
binding.commentText.setText(mention); binding.commentText.setText(mention);
@ -326,7 +362,7 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
return; return;
} }
onRefresh(); commentsAdapter.setLiked(commentModel, true);
} }
@Override @Override
@ -344,7 +380,7 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
return; return;
} }
onRefresh(); commentsAdapter.setLiked(commentModel, false);
} }
@Override @Override
@ -389,4 +425,14 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment impl
final NavDirections action = CommentsViewerFragmentDirections.actionGlobalProfileFragment(username); final NavDirections action = CommentsViewerFragmentDirections.actionGlobalProfileFragment(username);
NavHostFragment.findNavController(this).navigate(action); NavHostFragment.findNavController(this).navigate(action);
} }
private void stopCurrentExecutor() {
if (currentlyRunning != null) {
try {
currentlyRunning.cancel(true);
} catch (final Exception e) {
if (BuildConfig.DEBUG) Log.e(TAG, "", e);
}
}
}
} }

View File

@ -11,11 +11,10 @@ public class CommentModel {
private final ProfileModel profileModel; private final ProfileModel profileModel;
private final String id; private final String id;
private final String text; private final String text;
private final long likes; private long likes;
private final long timestamp; private final long timestamp;
private List<CommentModel> childCommentModels; private List<CommentModel> childCommentModels;
private final boolean liked; private boolean liked, hasNextPage;
private boolean hasNextPage;
private String endCursor; private String endCursor;
public CommentModel(final String id, public CommentModel(final String id,
@ -53,6 +52,11 @@ public class CommentModel {
return liked; return liked;
} }
public void setLiked(boolean liked) {
this.likes = liked ? likes + 1 : likes - 1;
this.liked = liked;
}
public ProfileModel getProfileModel() { public ProfileModel getProfileModel() {
return profileModel; return profileModel;
} }

View File

@ -31,6 +31,7 @@
<color name="dm_profile_button_color">#efefef</color> <color name="dm_profile_button_color">#efefef</color>
<color name="comment_selected">#888888</color> <color name="comment_selected">#888888</color>
<color name="comment_liked">#40FF69B4</color>
<color name="white">#FFFFFF</color> <color name="white">#FFFFFF</color>
<color name="white_a50">#80FFFFFF</color> <color name="white_a50">#80FFFFFF</color>