Repository: wordpress-mobile/WordPress-Android (language: kotlin)

Issue:
[Reader IA - Bug] Followed P2s feed not showing in drop-down
![image](https://github.com/wordpress-mobile/WordPress-Android/assets/47797566/e0725f07-6802-477c-9f39-d6bbe0baa8ea)

>While comparing the viewpager tabs and feeds content for before / after, I see a missing menu item for "Followed P2S" (that is a filterable with only Sites and no Tags elements, I think similarly to the Automattic item)

What I was trying to say here is (using the same user for the before/after):
- the "Followed P2s" item is missing in the after

About how to get the Followed P2s on your side, what follows is my best guess
>I think that menu is related to P2 the product sites that I think I created in that account from https://p2help.wordpress.com/getting-started/creating-a-new-workspace/ if it's useful to create the test scenario.

but I can share my test account with you if it's quicker (just dm me in slack 👍 )

_Originally posted by @develric in https://github.com/wordpress-mobile/WordPress-Android/issues/19856#issuecomment-1881059568_


Discussion hints:
We should also consider adding the analytics event for when this menu item is clicked.

Pull request: [Reader IA] Add "Followed P2s" item to dropdown menu
Fixes #19898 

-----

## To Test:

- Install JP and sign in with an account that has followed P2s and is not associated with A8C
- Open the Reader: you should see the `Followed P2s` item in the dropdown menu
<img width="211" alt="image" src="https://github.com/wordpress-mobile/WordPress-Android/assets/14964993/0343cc06-8f23-4965-aa8b-5f1a8731a00d">

- Tap the `Followed P2s` item: you should see the post list and blogs filter. You can compare it with web to check the expected behavior.
<img width="356" alt="Screenshot 2024-01-26 at 6 05 19 PM" src="https://github.com/wordpress-mobile/WordPress-Android/assets/14964993/9799fa0b-92ec-4e5a-9682-b9444f5ac1ce">

- Verify that the following events are tracked:
```
🔵 Tracked: reader_dropdown_menu_item_tapped, Properties: {"id":"p2"}
🔵 Tracked: reader_p2_shown
```

- Verify that filter blog also works as expected.

<!-- Test instructions per dependency update: https://github.com/wordpress-mobile/WordPress-Android/blob/trunk/docs/test_instructions_per_dependency_update.md -->

-----

## Regression Notes

1. Potential unintended areas of impact

    - None

2. What I did to test those areas of impact (or what existing automated tests I relied on)

    - Manual testing and updated unit tests

3. What automated tests I added (or what prevented me from doing so)

    --

-----

## PR Submission Checklist:

- [x] I have completed the Regression Notes.
- [x] I have considered adding accessibility improvements for my changes.
- [x] I have considered if this change warrants user-facing release notes and have added them to `RELEASE-NOTES.txt` if necessary.

-----

## UI Changes Testing Checklist:

- [ ] Portrait and landscape orientations.
- [ ] Light and dark modes.
- [ ] Fonts: Larger, smaller and bold text.
- [ ] High contrast.
- [ ] Talkback.
- [ ] Languages with large words or with letters/accents not frequently used in English.
- [ ] Right-to-left languages. (Even if translation isn’t complete, formatting should still respect the right-to-left layout)
- [ ] Large and small screen sizes. (Tablet and smaller phones)
- [ ] Multi-tasking: Split screen and Pop-up view. (Android 10 or higher)


Test command: ./gradlew :WordPress:testWordpressVanillaDebugUnitTest --tests "org.wordpress.android.ui.reader.utils.ReaderTopBarMenuHelperTest"

Fix patch:
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 445ac315c255..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,8 +144,10 @@
 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;
 import java.util.List;
 import java.util.Stack;
 
@@ -210,9 +212,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 HashSet<UpdateAction> mCurrentUpdateActions = new HashSet<>();
     /*
      * called by post adapter to load older posts when user scrolls to the last post
      */
@@ -957,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());
@@ -1180,9 +1182,12 @@ public void onShowCustomEmptyView(EmptyViewMessageType emptyViewMsgType) {
             }
         }
 
-        if (savedInstanceState != null && savedInstanceState.getBoolean(ReaderConstants.KEY_IS_REFRESHING)) {
-            mIsUpdating = true;
-            mRecyclerView.setRefreshing(true);
+        if (savedInstanceState != null) {
+            Serializable actions = savedInstanceState.getSerializable(ReaderConstants.KEY_CURRENT_UPDATE_ACTIONS);
+            if (actions instanceof HashSet<?>) {
+                mCurrentUpdateActions = (HashSet<UpdateAction>) actions;
+                updateProgressIndicators();
+            }
         }
 
         return rootView;
@@ -1699,7 +1704,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 +2309,7 @@ public void run() {
     }
 
     private boolean isUpdating() {
-        return mIsUpdating;
+        return mCurrentUpdateActions.size() > 0;
     }
 
     /*
@@ -2321,27 +2326,45 @@ private void showLoadingProgress(boolean showProgress) {
         }
     }
 
-    private void setIsUpdating(boolean isUpdating, UpdateAction updateAction) {
-        if (!isAdded() || mIsUpdating == isUpdating) {
-            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());
         }
     }
 
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<ReaderTag>()) { 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<ReaderTag>): MenuElementData.Item.SubMenu {
         val customListsMenuItems = mutableListOf<MenuElementData.Item.Single>()
         customLists.forEach { index, readerTag ->


Test patch:
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,