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

Issue:
[Reader] Add "Tags" item do dropdown menu
![Image](https://github.com/wordpress-mobile/WordPress-Android/assets/14964993/fb11745b-0d19-4461-bd12-3a4cec93da3e)


Discussion hints:
nan

Pull request: [Reader] Add "Tags" to dropdown menu
Fixes #20576

-----

## To Test:

- Install JP and sign in.
- Open "Reader".
- 🔍 Open the dropdown menu: there should be a "Tags" item with the correct icon (design: p1712144397494019/1712076563.723409-slack-C05N140C8H5) 
<img width="198" alt="image" src="https://github.com/wordpress-mobile/WordPress-Android/assets/14964993/4f9306ec-cffd-4f0d-9434-caf318674c7a">

- 🔍 Tap the "Tags" item: an empty screen should be loaded (empty state and posts lists will be handled in separate tickets).
- 🔍 Open the other feed items: they should be working as expected.
- 🔍 Inspect the local DB: the "Tags" item only exists in memory, so it shouldn't be added to the local DB.
- 🔍 Create a new custom list in the web "Reader" and verify it's shown in the app to make sure the back-end integration is still working as expected.

-----

## 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.

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.

-----

## Testing Checklist (strike-out the not-applying and unnecessary ones):

- [ ] WordPress.com sites and self-hosted Jetpack sites.
- [ ] 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.viewmodels.ReaderViewModelTest"

Fix patch:
diff --git a/WordPress/src/main/java/org/wordpress/android/models/ReaderTag.java b/WordPress/src/main/java/org/wordpress/android/models/ReaderTag.java
index a8e8018e9814..dbd7b537e8cf 100644
--- a/WordPress/src/main/java/org/wordpress/android/models/ReaderTag.java
+++ b/WordPress/src/main/java/org/wordpress/android/models/ReaderTag.java
@@ -184,6 +184,10 @@ public boolean isBookmarked() {
         return tagType == ReaderTagType.BOOKMARKED;
     }
 
+    public boolean isTags() {
+        return tagType == ReaderTagType.TAGS;
+    }
+
     public boolean isDiscover() {
         return tagType == ReaderTagType.DEFAULT && getEndpoint().endsWith(DISCOVER_PATH);
     }
diff --git a/WordPress/src/main/java/org/wordpress/android/models/ReaderTagType.java b/WordPress/src/main/java/org/wordpress/android/models/ReaderTagType.java
index d7d84f191168..cb3486efc523 100644
--- a/WordPress/src/main/java/org/wordpress/android/models/ReaderTagType.java
+++ b/WordPress/src/main/java/org/wordpress/android/models/ReaderTagType.java
@@ -7,7 +7,8 @@ public enum ReaderTagType {
     CUSTOM_LIST,
     SEARCH,
     INTERESTS,
-    DISCOVER_POST_CARDS;
+    DISCOVER_POST_CARDS,
+    TAGS;
 
     private static final int INT_DEFAULT = 0;
     private static final int INT_FOLLOWED = 1;
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/usecases/LoadReaderTabsUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/reader/usecases/LoadReaderItemsUseCase.kt
similarity index 59%
rename from WordPress/src/main/java/org/wordpress/android/ui/reader/usecases/LoadReaderTabsUseCase.kt
rename to WordPress/src/main/java/org/wordpress/android/ui/reader/usecases/LoadReaderItemsUseCase.kt
index e6d6aea13e5c..0194392aa29d 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/usecases/LoadReaderTabsUseCase.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/usecases/LoadReaderItemsUseCase.kt
@@ -3,24 +3,29 @@ package org.wordpress.android.ui.reader.usecases
 import dagger.Reusable
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.withContext
+import org.wordpress.android.R
 import org.wordpress.android.datasets.ReaderTagTable
+import org.wordpress.android.models.ReaderTag
 import org.wordpress.android.models.ReaderTagList
+import org.wordpress.android.models.ReaderTagType
 import org.wordpress.android.models.containsFollowingTag
 import org.wordpress.android.modules.BG_THREAD
 import org.wordpress.android.ui.reader.utils.ReaderUtils
 import org.wordpress.android.ui.reader.utils.ReaderUtilsWrapper
+import org.wordpress.android.util.StringProvider
 import javax.inject.Inject
 import javax.inject.Named
 
 /**
- * Loads list of tags that should be displayed as tabs in the entry-point Reader screen.
+ * Loads list of items that should be displayed in the Reader dropdown menu.
  */
 @Reusable
-class LoadReaderTabsUseCase @Inject constructor(
+class LoadReaderItemsUseCase @Inject constructor(
     @Named(BG_THREAD) private val bgDispatcher: CoroutineDispatcher,
-    private val readerUtilsWrapper: ReaderUtilsWrapper
+    private val readerUtilsWrapper: ReaderUtilsWrapper,
+    private val stringProvider: StringProvider,
 ) {
-    suspend fun loadTabs(): ReaderTagList {
+    suspend fun load(): ReaderTagList {
         return withContext(bgDispatcher) {
             val tagList = ReaderTagTable.getDefaultTags()
 
@@ -28,9 +33,18 @@ class LoadReaderTabsUseCase @Inject constructor(
             for users who created custom lists in the past.*/
             tagList.addAll(ReaderTagTable.getCustomListTags())
 
-            tagList.addAll(ReaderTagTable.getBookmarkTags()) // Add "Saved" tab manually
+            tagList.addAll(ReaderTagTable.getBookmarkTags()) // Add "Saved" item manually
 
-            // Add "Following" tab manually when on self-hosted site
+            // Add "Tags" item manually
+            tagList.add(ReaderTag(
+                "",
+                stringProvider.getString(R.string.reader_tags_display_name),
+                stringProvider.getString(R.string.reader_tags_display_name),
+                "",
+                ReaderTagType.TAGS
+            ))
+
+            // Add "Subscriptions" item manually when on self-hosted site
             if (!tagList.containsFollowingTag()) {
                 tagList.add(readerUtilsWrapper.getDefaultTagFromDbOrCreateInMemory())
             }
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 26e2f876a83c..466c642f271f 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
@@ -37,6 +37,9 @@ class ReaderTopBarMenuHelper @Inject constructor() {
                     text = readerTagsList[followedP2sIndex].tagTitle,
                 ))
             }
+            readerTagsList.indexOrNull { it.isTags }?.let { tagsIndex ->
+                add(createTagsItem(getMenuItemIdFromReaderTagIndex(tagsIndex)))
+            }
             readerTagsList
                 .foldIndexed(SparseArrayCompat<ReaderTag>()) { index, sparseArray, readerTag ->
                     if (readerTag.tagType == ReaderTagType.CUSTOM_LIST) {
@@ -98,6 +101,14 @@ class ReaderTopBarMenuHelper @Inject constructor() {
         )
     }
 
+    private fun createTagsItem(id: String): MenuElementData.Item.Single {
+        return MenuElementData.Item.Single(
+            id = id,
+            text = UiString.UiStringRes(R.string.reader_dropdown_menu_tags),
+            leadingIcon = R.drawable.ic_reader_tags_24dp,
+        )
+    }
+
     private fun createCustomListsItem(customLists: SparseArrayCompat<ReaderTag>): MenuElementData.Item.SubMenu {
         val customListsMenuItems = mutableListOf<MenuElementData.Item.Single>()
         customLists.forEach { index, readerTag ->
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/viewmodels/ReaderViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/reader/viewmodels/ReaderViewModel.kt
index 5806d169043a..71da3851da95 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/viewmodels/ReaderViewModel.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/viewmodels/ReaderViewModel.kt
@@ -39,7 +39,7 @@ import org.wordpress.android.ui.reader.subfilter.SubfilterListItem
 import org.wordpress.android.ui.reader.tracker.ReaderTab
 import org.wordpress.android.ui.reader.tracker.ReaderTracker
 import org.wordpress.android.ui.reader.tracker.ReaderTrackerType.MAIN_READER
-import org.wordpress.android.ui.reader.usecases.LoadReaderTabsUseCase
+import org.wordpress.android.ui.reader.usecases.LoadReaderItemsUseCase
 import org.wordpress.android.ui.reader.utils.DateProvider
 import org.wordpress.android.ui.reader.utils.ReaderTopBarMenuHelper
 import org.wordpress.android.ui.reader.viewmodels.ReaderViewModel.ReaderUiState.ContentUiState
@@ -69,7 +69,7 @@ class ReaderViewModel @Inject constructor(
     @Named(BG_THREAD) private val bgDispatcher: CoroutineDispatcher,
     private val appPrefsWrapper: AppPrefsWrapper,
     private val dateProvider: DateProvider,
-    private val loadReaderTabsUseCase: LoadReaderTabsUseCase,
+    private val loadReaderItemsUseCase: LoadReaderItemsUseCase,
     private val readerTracker: ReaderTracker,
     private val accountStore: AccountStore,
     private val quickStartRepository: QuickStartRepository,
@@ -140,7 +140,7 @@ class ReaderViewModel @Inject constructor(
     @JvmOverloads
     fun loadTabs(savedInstanceState: Bundle? = null) {
         launch {
-            val tagList = loadReaderTabsUseCase.loadTabs()
+            val tagList = loadReaderItemsUseCase.load()
             if (tagList.isNotEmpty() && readerTagsList != tagList) {
                 updateReaderTagsList(tagList)
                 updateTopBarUiState(savedInstanceState)
diff --git a/WordPress/src/main/res/drawable/ic_reader_tags_24dp.xml b/WordPress/src/main/res/drawable/ic_reader_tags_24dp.xml
new file mode 100644
index 000000000000..66e347a7ec51
--- /dev/null
+++ b/WordPress/src/main/res/drawable/ic_reader_tags_24dp.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M20.1,11.2L13.4,4.5C13.3,4.4 13.1,4.3 12.9,4.3H5C4.6,4.2 4.2,4.6 4.2,5V12.8C4.2,13 4.3,13.2 4.4,13.3L11.1,20C11.3,20.2 11.6,20.4 11.8,20.5C12,20.6 12.4,20.7 12.7,20.7C13,20.7 13.3,20.6 13.6,20.5C13.9,20.4 14.1,20.2 14.4,20L20,14.4C20.4,14 20.7,13.4 20.7,12.8C20.8,12.2 20.5,11.6 20.1,11.2ZM19,13.4L13.4,19C13.3,19.1 13.2,19.1 13.1,19.2C12.9,19.3 12.7,19.3 12.5,19.2C12.4,19.2 12.3,19.1 12.2,19L5.7,12.5V5.8H12.5L19,12.3C19.2,12.5 19.2,12.7 19.2,12.9C19.2,13 19.2,13.2 19,13.4ZM9,8C8.4,8 8,8.4 8,9C8,9.6 8.4,10 9,10C9.6,10 10,9.6 10,9C10,8.4 9.6,8 9,8Z"
+      android:fillColor="#000000"/>
+</vector>
diff --git a/WordPress/src/main/res/values/strings.xml b/WordPress/src/main/res/values/strings.xml
index a98f8033b020..6969e14b0513 100644
--- a/WordPress/src/main/res/values/strings.xml
+++ b/WordPress/src/main/res/values/strings.xml
@@ -1690,6 +1690,7 @@
     <string name="reader_dropdown_menu_saved">Saved</string>
     <string name="reader_dropdown_menu_liked">Liked</string>
     <string name="reader_dropdown_menu_automattic">Automattic</string>
+    <string name="reader_dropdown_menu_tags">Tags</string>
     <string name="reader_dropdown_menu_lists">Lists</string>
     <string name="reader_filter_chip_blog_zero">0 Blogs</string>
     <string name="reader_filter_chip_blog_one">1 Blog</string>
@@ -2104,6 +2105,7 @@
     <string name="reader_discover_display_name">Discover</string>
     <string name="reader_my_likes_display_name">Likes</string>
     <string name="reader_subscribed_display_name">Subscribed</string>
+    <string name="reader_tags_display_name">Tags</string>
 
     <!-- timespan shown for posts/comments published within the past 60 seconds -->
     <string name="timespan_now" tools:ignore="UnusedResources">now</string>


Test patch:
diff --git a/WordPress/src/test/java/org/wordpress/android/ui/reader/viewmodels/ReaderViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/reader/viewmodels/ReaderViewModelTest.kt
index 2f437f24465c..bc6e73735645 100644
--- a/WordPress/src/test/java/org/wordpress/android/ui/reader/viewmodels/ReaderViewModelTest.kt
+++ b/WordPress/src/test/java/org/wordpress/android/ui/reader/viewmodels/ReaderViewModelTest.kt
@@ -33,7 +33,7 @@ import org.wordpress.android.ui.quickstart.QuickStartEvent
 import org.wordpress.android.ui.quickstart.QuickStartType
 import org.wordpress.android.ui.reader.utils.ReaderTopBarMenuHelper
 import org.wordpress.android.ui.reader.tracker.ReaderTracker
-import org.wordpress.android.ui.reader.usecases.LoadReaderTabsUseCase
+import org.wordpress.android.ui.reader.usecases.LoadReaderItemsUseCase
 import org.wordpress.android.ui.reader.utils.DateProvider
 import org.wordpress.android.ui.reader.viewmodels.ReaderViewModel.QuickStartReaderPrompt
 import org.wordpress.android.ui.reader.viewmodels.ReaderViewModel.ReaderUiState
@@ -59,7 +59,7 @@ class ReaderViewModelTest : BaseUnitTest() {
     lateinit var dateProvider: DateProvider
 
     @Mock
-    lateinit var loadReaderTabsUseCase: LoadReaderTabsUseCase
+    lateinit var loadReaderItemsUseCase: LoadReaderItemsUseCase
 
     @Mock
     lateinit var readerTracker: ReaderTracker
@@ -100,7 +100,7 @@ class ReaderViewModelTest : BaseUnitTest() {
             testDispatcher(),
             appPrefsWrapper,
             dateProvider,
-            loadReaderTabsUseCase,
+            loadReaderItemsUseCase,
             readerTracker,
             accountStore,
             quickStartRepository,
@@ -156,7 +156,7 @@ class ReaderViewModelTest : BaseUnitTest() {
         viewModel.uiState.observeForever {
             state = it
         }
-        whenever(loadReaderTabsUseCase.loadTabs()).thenReturn(ReaderTagList())
+        whenever(loadReaderItemsUseCase.load()).thenReturn(ReaderTagList())
         // Act
         triggerContentDisplay()
         // Assert
@@ -562,14 +562,14 @@ class ReaderViewModelTest : BaseUnitTest() {
 
     private fun <T> testWithEmptyTags(block: suspend CoroutineScope.() -> T) {
         test {
-            whenever(loadReaderTabsUseCase.loadTabs()).thenReturn(emptyReaderTagList)
+            whenever(loadReaderItemsUseCase.load()).thenReturn(emptyReaderTagList)
             block()
         }
     }
 
     private fun <T> testWithNonEmptyTags(block: suspend CoroutineScope.() -> T) {
         test {
-            whenever(loadReaderTabsUseCase.loadTabs()).thenReturn(nonEmptyReaderTagList)
+            whenever(loadReaderItemsUseCase.load()).thenReturn(nonEmptyReaderTagList)
             block()
         }
     }
@@ -579,7 +579,7 @@ class ReaderViewModelTest : BaseUnitTest() {
         block: suspend CoroutineScope.() -> T
     ) {
         test {
-            whenever(loadReaderTabsUseCase.loadTabs()).thenReturn(readerTags)
+            whenever(loadReaderItemsUseCase.load()).thenReturn(readerTags)
             block()
         }
     }