From 6a3da3e85162e6636e6a2e8b10687a42e459fa74 Mon Sep 17 00:00:00 2001 From: Renan Lukas <14964993+RenanLukas@users.noreply.github.com> Date: Fri, 26 Jan 2024 17:53:55 -0300 Subject: [PATCH 1/4] Add Followed P2s item do Reader dropdown menu --- .../ui/reader/tracker/ReaderTracker.kt | 1 + .../ui/reader/utils/ReaderTopBarMenuHelper.kt | 13 +++++++++++ .../utils/ReaderTopBarMenuHelperTest.kt | 23 ++++++++++++++----- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/tracker/ReaderTracker.kt b/WordPress/src/main/java/org/wordpress/android/ui/reader/tracker/ReaderTracker.kt index 15822295422c..b40dfc0380ee 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/tracker/ReaderTracker.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/tracker/ReaderTracker.kt @@ -388,6 +388,7 @@ class ReaderTracker @Inject constructor( readerTag.isPostsILike -> "liked" readerTag.isA8C -> "a8c" readerTag.isListTopic -> "list" + readerTag.isP2 -> "p2" else -> null }?.let { trackingId -> analyticsTrackerWrapper.track( diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/utils/ReaderTopBarMenuHelper.kt b/WordPress/src/main/java/org/wordpress/android/ui/reader/utils/ReaderTopBarMenuHelper.kt index 05f6f28d8c1b..26e2f876a83c 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/utils/ReaderTopBarMenuHelper.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/utils/ReaderTopBarMenuHelper.kt @@ -31,6 +31,12 @@ class ReaderTopBarMenuHelper @Inject constructor() { readerTagsList.indexOrNull { it.isA8C }?.let { a8cIndex -> add(createAutomatticItem(getMenuItemIdFromReaderTagIndex(a8cIndex))) } + readerTagsList.indexOrNull { it.isP2 }?.let { followedP2sIndex -> + add(createFollowedP2sItem( + id = getMenuItemIdFromReaderTagIndex(followedP2sIndex), + text = readerTagsList[followedP2sIndex].tagTitle, + )) + } readerTagsList .foldIndexed(SparseArrayCompat()) { index, sparseArray, readerTag -> if (readerTag.tagType == ReaderTagType.CUSTOM_LIST) { @@ -85,6 +91,13 @@ class ReaderTopBarMenuHelper @Inject constructor() { ) } + private fun createFollowedP2sItem(id: String, text: String): MenuElementData.Item.Single { + return MenuElementData.Item.Single( + id = id, + text = UiString.UiStringText(text), + ) + } + private fun createCustomListsItem(customLists: SparseArrayCompat): MenuElementData.Item.SubMenu { val customListsMenuItems = mutableListOf() customLists.forEach { index, readerTag -> diff --git a/WordPress/src/test/java/org/wordpress/android/ui/reader/utils/ReaderTopBarMenuHelperTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/reader/utils/ReaderTopBarMenuHelperTest.kt index b8a4180dff9d..0279f25bee2d 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/reader/utils/ReaderTopBarMenuHelperTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/reader/utils/ReaderTopBarMenuHelperTest.kt @@ -23,9 +23,10 @@ class ReaderTopBarMenuHelperTest { add(mockSavedTag()) // item 2 add(mockLikedTag()) // item 3 add(mockA8CTag()) // item 4 - add(createCustomListTag("custom-list-1")) // item 5 - add(createCustomListTag("custom-list-2")) // item 6 - add(createCustomListTag("custom-list-3")) // item 7 + add(mockFollowedP2sTag()) // item 5 + add(createCustomListTag("custom-list-1")) // item 6 + add(createCustomListTag("custom-list-2")) // item 7 + add(createCustomListTag("custom-list-3")) // item 8 } val menu = helper.createMenu(tags) @@ -46,18 +47,21 @@ class ReaderTopBarMenuHelperTest { val a8cItem = menu.findSingleItem { it.id == "4" }!! assertThat(a8cItem.text).isEqualTo(UiStringRes(R.string.reader_dropdown_menu_automattic)) + val followedP2sItem = menu.findSingleItem { it.id == "5" }!! + assertThat(followedP2sItem.text).isEqualTo(UiStringText("Followed P2s")) + assertThat(menu).contains(MenuElementData.Divider) val customListsItem = menu.findSubMenu()!! assertThat(customListsItem.text).isEqualTo(UiStringRes(R.string.reader_dropdown_menu_lists)) - val customList1Item = customListsItem.children.findSingleItem { it.id == "5" }!! + val customList1Item = customListsItem.children.findSingleItem { it.id == "6" }!! assertThat(customList1Item.text).isEqualTo(UiStringText("custom-list-1")) - val customList2Item = customListsItem.children.findSingleItem { it.id == "6" }!! + val customList2Item = customListsItem.children.findSingleItem { it.id == "7" }!! assertThat(customList2Item.text).isEqualTo(UiStringText("custom-list-2")) - val customList3Item = customListsItem.children.findSingleItem { it.id == "7" }!! + val customList3Item = customListsItem.children.findSingleItem { it.id == "8" }!! assertThat(customList3Item.text).isEqualTo(UiStringText("custom-list-3")) } @@ -264,6 +268,13 @@ class ReaderTopBarMenuHelperTest { } } + private fun mockFollowedP2sTag(): ReaderTag { + return mock { + on { isP2 } doReturn true + on { tagTitle } doReturn "Followed P2s" + } + } + private fun createCustomListTag(title: String): ReaderTag { return ReaderTag( title, From af5dc4d7bafc4deb94e3f876d67343ae13e696dc Mon Sep 17 00:00:00 2001 From: Renan Lukas <14964993+RenanLukas@users.noreply.github.com> Date: Mon, 29 Jan 2024 18:49:16 -0300 Subject: [PATCH 2/4] Fix PR comment: swipe to refresh loading never disappears --- .../wordpress/android/ui/reader/ReaderPostListFragment.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java index 445ac315c255..0c4639027bea 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java @@ -2323,6 +2323,9 @@ private void showLoadingProgress(boolean showProgress) { private void setIsUpdating(boolean isUpdating, UpdateAction updateAction) { if (!isAdded() || mIsUpdating == isUpdating) { + if (!isUpdating) { + mRecyclerView.setRefreshing(false); + } return; } From 62a1ee7775b0cfc5e0e4fb310805c5a2b8525aa2 Mon Sep 17 00:00:00 2001 From: Thomas Horta Date: Tue, 30 Jan 2024 11:52:43 -0300 Subject: [PATCH 3/4] Store post update state in a Set for reliable UI representation --- .../ui/reader/ReaderPostListFragment.java | 59 ++++++++++++------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java index 0c4639027bea..12026cc56d21 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java @@ -146,6 +146,7 @@ import java.util.ArrayList; import java.util.EnumSet; +import java.util.HashSet; import java.util.List; import java.util.Stack; @@ -210,9 +211,9 @@ public class ReaderPostListFragment extends ViewPagerFragment private int mPostSearchAdapterPos; private int mSiteSearchAdapterPos; private int mSearchTabsPos = NO_POSITION; - private boolean mIsUpdating; private boolean mIsFilterableScreen; private boolean mIsFiltered = false; + @NonNull private final HashSet mCurrentUpdateActions = new HashSet<>(); /* * called by post adapter to load older posts when user scrolls to the last post */ @@ -1181,8 +1182,7 @@ public void onShowCustomEmptyView(EmptyViewMessageType emptyViewMsgType) { } if (savedInstanceState != null && savedInstanceState.getBoolean(ReaderConstants.KEY_IS_REFRESHING)) { - mIsUpdating = true; - mRecyclerView.setRefreshing(true); + setIsUpdating(true, UpdateAction.REQUEST_NEWER); } return rootView; @@ -1699,7 +1699,7 @@ private void setEmptyTitleDescriptionAndButton(boolean requestFailed) { setEmptyTitleAndDescriptionForBookmarksList(); return; } else if (!NetworkUtils.isNetworkAvailable(getActivity())) { - mIsUpdating = false; + clearCurrentUpdateActions(); title = getString(R.string.reader_empty_posts_no_connection); } else if (requestFailed) { if (isSearching()) { @@ -2304,7 +2304,7 @@ public void run() { } private boolean isUpdating() { - return mIsUpdating; + return mCurrentUpdateActions.size() > 0; } /* @@ -2321,30 +2321,45 @@ private void showLoadingProgress(boolean showProgress) { } } - private void setIsUpdating(boolean isUpdating, UpdateAction updateAction) { - if (!isAdded() || mIsUpdating == isUpdating) { - if (!isUpdating) { - mRecyclerView.setRefreshing(false); - } - return; - } + private void clearCurrentUpdateActions() { + if (!isAdded() || !isUpdating()) return; - if (updateAction == UpdateAction.REQUEST_OLDER) { - // show/hide progress bar at bottom if these are older posts - showLoadingProgress(isUpdating); - } else if (isUpdating) { - // show swipe-to-refresh if update started - mRecyclerView.setRefreshing(true); + mCurrentUpdateActions.clear(); + updateProgressIndicators(); + } + + private void setIsUpdating(boolean isUpdating, @NonNull UpdateAction updateAction) { + if (!isAdded()) return; + + boolean isUiUpdateNeeded; + if (isUpdating) { + isUiUpdateNeeded = mCurrentUpdateActions.add(updateAction); } else { - // hide swipe-to-refresh progress if update is complete + isUiUpdateNeeded = mCurrentUpdateActions.remove(updateAction); + } + + if (isUiUpdateNeeded) updateProgressIndicators(); + } + + private void updateProgressIndicators() { + if (!isUpdating()) { + // when there's no update in progress, hide the bottom and swipe-to-refresh progress bars + showLoadingProgress(false); mRecyclerView.setRefreshing(false); + } else if (mCurrentUpdateActions.size() == 1 && mCurrentUpdateActions.contains(UpdateAction.REQUEST_OLDER)) { + // if only older posts are being updated, show only the bottom progress bar + showLoadingProgress(true); + mRecyclerView.setRefreshing(false); + } else { + // if anything else is being updated, show only the swipe-to-refresh progress bar + showLoadingProgress(false); + mRecyclerView.setRefreshing(true); } - mIsUpdating = isUpdating; // if swipe-to-refresh isn't active, keep it disabled during an update - this prevents // doing a refresh while another update is already in progress - if (mRecyclerView != null && !mRecyclerView.isRefreshing()) { - mRecyclerView.setSwipeToRefreshEnabled(!isUpdating && isSwipeToRefreshSupported()); + if (!mRecyclerView.isRefreshing()) { + mRecyclerView.setSwipeToRefreshEnabled(!isUpdating() && isSwipeToRefreshSupported()); } } From dfde3f826d62e3867f29ac89ffed5d9359aa055a Mon Sep 17 00:00:00 2001 From: Thomas Horta Date: Tue, 30 Jan 2024 17:11:20 -0300 Subject: [PATCH 4/4] Replace the IS_REFRESHING bundle state with CURRENT_UPDATE_ACTIONS --- .../android/ui/reader/ReaderConstants.java | 2 +- .../android/ui/reader/ReaderPostListFragment.java | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderConstants.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderConstants.java index 504d71f1de01..c0798c5824ca 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderConstants.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderConstants.java @@ -67,7 +67,7 @@ public class ReaderConstants { static final String KEY_FIRST_LOAD = "first_load"; static final String KEY_ACTIVITY_TITLE = "activity_title"; static final String KEY_TRACKED_POSITIONS = "tracked_positions"; - static final String KEY_IS_REFRESHING = "is_refreshing"; + static final String KEY_CURRENT_UPDATE_ACTIONS = "current_update_actions"; static final String KEY_ACTIVE_SEARCH_TAB = "active_search_tab"; static final String KEY_SITE_ID = "site_id"; diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java index 12026cc56d21..763d944c130c 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java @@ -144,6 +144,7 @@ import org.wordpress.android.widgets.RecyclerItemDecoration; import org.wordpress.android.widgets.WPSnackbar; +import java.io.Serializable; import java.util.ArrayList; import java.util.EnumSet; import java.util.HashSet; @@ -213,7 +214,7 @@ public class ReaderPostListFragment extends ViewPagerFragment private int mSearchTabsPos = NO_POSITION; private boolean mIsFilterableScreen; private boolean mIsFiltered = false; - @NonNull private final HashSet mCurrentUpdateActions = new HashSet<>(); + @NonNull private HashSet mCurrentUpdateActions = new HashSet<>(); /* * called by post adapter to load older posts when user scrolls to the last post */ @@ -958,8 +959,8 @@ public void onSaveInstanceState(@NonNull Bundle outState) { outState.putBoolean(ReaderConstants.KEY_ALREADY_REQUESTED, mHasRequestedPosts); outState.putBoolean(ReaderConstants.KEY_ALREADY_UPDATED, mHasUpdatedPosts); outState.putBoolean(ReaderConstants.KEY_FIRST_LOAD, mFirstLoad); + outState.putSerializable(ReaderConstants.KEY_CURRENT_UPDATE_ACTIONS, mCurrentUpdateActions); if (mRecyclerView != null) { - outState.putBoolean(ReaderConstants.KEY_IS_REFRESHING, mRecyclerView.isRefreshing()); outState.putInt(ReaderConstants.KEY_RESTORE_POSITION, getCurrentPosition()); } outState.putSerializable(ReaderConstants.ARG_POST_LIST_TYPE, getPostListType()); @@ -1181,8 +1182,12 @@ public void onShowCustomEmptyView(EmptyViewMessageType emptyViewMsgType) { } } - if (savedInstanceState != null && savedInstanceState.getBoolean(ReaderConstants.KEY_IS_REFRESHING)) { - setIsUpdating(true, UpdateAction.REQUEST_NEWER); + if (savedInstanceState != null) { + Serializable actions = savedInstanceState.getSerializable(ReaderConstants.KEY_CURRENT_UPDATE_ACTIONS); + if (actions instanceof HashSet) { + mCurrentUpdateActions = (HashSet) actions; + updateProgressIndicators(); + } } return rootView;