From f4109f38a84192e8f0085c4bc8fb5663deaa7f16 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Sat, 15 Apr 2017 12:28:22 +0200 Subject: [PATCH] Allow pull-to-refresh in thread view (closes #73) --- .../keylesspalace/tusky/ThreadAdapter.java | 31 ++++++++++++++++--- .../tusky/ViewThreadFragment.java | 20 +++++++++--- .../main/res/layout/fragment_view_thread.xml | 14 ++++++--- 3 files changed, 53 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/ThreadAdapter.java b/app/src/main/java/com/keylesspalace/tusky/ThreadAdapter.java index fea024558..7af6d13f0 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ThreadAdapter.java +++ b/app/src/main/java/com/keylesspalace/tusky/ThreadAdapter.java @@ -65,20 +65,43 @@ class ThreadAdapter extends RecyclerView.Adapter implements AdapterItemRemover { notifyItemRemoved(position); } - int insertStatus(Status status) { + int setStatus(Status status) { + if (statuses.size() > 0 && statuses.get(statusIndex).equals(status)) { + // Do not add this status on refresh, it's already in there. + statuses.set(statusIndex, status); + return statusIndex; + } int i = statusIndex; statuses.add(i, status); notifyItemInserted(i); return i; } - void addAncestors(List ancestors) { + void setContext(List ancestors, List descendants) { + Status mainStatus = null; + + // In case of refresh, remove old ancestors and descendants first. We'll remove all blindly, + // as we have no guarantee on their order to be the same as before + int old_size = statuses.size(); + if (old_size > 0) { + mainStatus = statuses.get(statusIndex); + statuses.clear(); + notifyItemRangeRemoved(0, old_size); + } + + // Insert newly fetched ancestors statusIndex = ancestors.size(); statuses.addAll(0, ancestors); notifyItemRangeInserted(0, statusIndex); - } - void addDescendants(List descendants) { + if (mainStatus != null) { + // In case we needed to delete everything (which is way easier than deleting + // everything except one), re-insert the remaining status here. + statuses.add(statusIndex, mainStatus); + notifyItemInserted(statusIndex); + } + + // Insert newly fetched descendants int end = statuses.size(); statuses.addAll(descendants); notifyItemRangeInserted(end, descendants.size()); diff --git a/app/src/main/java/com/keylesspalace/tusky/ViewThreadFragment.java b/app/src/main/java/com/keylesspalace/tusky/ViewThreadFragment.java index 4101b44a1..67952669b 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ViewThreadFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/ViewThreadFragment.java @@ -21,6 +21,7 @@ import android.os.Bundle; import android.support.annotation.Nullable; import android.support.design.widget.Snackbar; import android.support.v4.content.ContextCompat; +import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.widget.DividerItemDecoration; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; @@ -34,9 +35,11 @@ import com.keylesspalace.tusky.entity.StatusContext; import retrofit2.Call; import retrofit2.Callback; -public class ViewThreadFragment extends SFragment implements StatusActionListener { +public class ViewThreadFragment extends SFragment implements + SwipeRefreshLayout.OnRefreshListener, StatusActionListener { private static final String TAG = "ViewThreadFragment"; + private SwipeRefreshLayout swipeRefreshLayout; private RecyclerView recyclerView; private ThreadAdapter adapter; private String thisThreadsStatusId; @@ -56,6 +59,9 @@ public class ViewThreadFragment extends SFragment implements StatusActionListene View rootView = inflater.inflate(R.layout.fragment_view_thread, container, false); Context context = getContext(); + swipeRefreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.swipe_refresh_layout); + swipeRefreshLayout.setOnRefreshListener(this); + recyclerView = (RecyclerView) rootView.findViewById(R.id.recycler_view); recyclerView.setHasFixedSize(true); LinearLayoutManager layoutManager = new LinearLayoutManager(context); @@ -86,7 +92,7 @@ public class ViewThreadFragment extends SFragment implements StatusActionListene @Override public void onResponse(Call call, retrofit2.Response response) { if (response.isSuccessful()) { - int position = adapter.insertStatus(response.body()); + int position = adapter.setStatus(response.body()); recyclerView.scrollToPosition(position); } else { onThreadRequestFailure(id); @@ -109,10 +115,10 @@ public class ViewThreadFragment extends SFragment implements StatusActionListene @Override public void onResponse(Call call, retrofit2.Response response) { if (response.isSuccessful()) { + swipeRefreshLayout.setRefreshing(false); StatusContext context = response.body(); - adapter.addAncestors(context.ancestors); - adapter.addDescendants(context.descendants); + adapter.setContext(context.ancestors, context.descendants); } else { onThreadRequestFailure(id); } @@ -128,6 +134,7 @@ public class ViewThreadFragment extends SFragment implements StatusActionListene private void onThreadRequestFailure(final String id) { View view = getView(); + swipeRefreshLayout.setRefreshing(false); if (view != null) { Snackbar.make(view, R.string.error_generic, Snackbar.LENGTH_LONG) .setAction(R.string.action_retry, new View.OnClickListener() { @@ -143,6 +150,11 @@ public class ViewThreadFragment extends SFragment implements StatusActionListene } } + public void onRefresh() { + sendStatusRequest(thisThreadsStatusId); + sendThreadRequest(thisThreadsStatusId); + } + public void onReply(int position) { super.reply(adapter.getItem(position)); } diff --git a/app/src/main/res/layout/fragment_view_thread.xml b/app/src/main/res/layout/fragment_view_thread.xml index 732fbb63f..3616a48f6 100644 --- a/app/src/main/res/layout/fragment_view_thread.xml +++ b/app/src/main/res/layout/fragment_view_thread.xml @@ -1,7 +1,13 @@ - \ No newline at end of file + android:layout_height="match_parent"> + + \ No newline at end of file