Fixes #20096

-----

## Description
This PR navigates non-admin users that cannot moderate a comment directly to the comments reader view

## To Test:
### A. Non-admin user
1. Create a post with an authorised user and add some comments
2. Log in with another user in the Jetpack app that does not administer the blog from (1)
3. Search and follow the blog from (1)
4. Find the post from (1) and [follow the conversation in the reader enabling notifications](https://github.com/wordpress-mobile/WordPress-Android/assets/304044/49860ff1-4afe-4fc8-b8bd-ea3f8e965648)
5. Write another comment as the authorised user on the post from (1) (e.g. on the web)
6. **Verify** that you receive a notification for that comment
7. Tap on the comment notification from 3 **in the notifications list**
8. **Expect** the Reader Comments screen to appear scrolled (if needed) to the comment from 3.

### B. Admin user ( Regression )
1. Log in with an admin user
2. Open the notifications in the app
3. Navigate to Notifications.
4. Tap on the comment notification from A5 **in the notifications list**
5. **Expect** the Comment Detail screen to appear were the user can moderate the comment

### C. Push notifications
Repeat (A) and (B) above by tapping on the **system push notification**

-----

## Regression Notes

1. Potential unintended areas of impact

    - Notifications detail view

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)

    - Added `NotificationsListViewModelTest` with the logic for opening the comment in the reader

-----

## 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:

- [ ] 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)

🤖 Bring the user straight to the comments reader view
Non-admin users that cannot moderate comment should skip the comment details screen and be directed straight to the comments reader view

      Fails

      :no_entry_sign:

  Please add a type label to this issue. e.g. '[Type] Enhancement'

      :no_entry_sign:
      Please add a feature label to this issue. e.g. 'Stats'

  Generated by :no_entry_sign: dangerJS

Thanks for reporting! 👍

diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java
index 6d67608adebb..3d432ded823f 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java
@@ -108,6 +108,7 @@
 import org.wordpress.android.ui.mysite.cards.quickstart.QuickStartRepository;
 import org.wordpress.android.ui.notifications.NotificationEvents;
 import org.wordpress.android.ui.notifications.NotificationsListFragment;
+import org.wordpress.android.ui.notifications.NotificationsListViewModel;
 import org.wordpress.android.ui.notifications.SystemNotificationsTracker;
 import org.wordpress.android.ui.notifications.adapters.NotesAdapter;
 import org.wordpress.android.ui.notifications.receivers.NotificationsPendingDraftsReceiver;
@@ -127,7 +128,9 @@
 import org.wordpress.android.ui.prefs.privacy.banner.PrivacyBannerFragment;
 import org.wordpress.android.ui.quickstart.QuickStartMySitePrompts;
 import org.wordpress.android.ui.quickstart.QuickStartTracker;
+import org.wordpress.android.ui.reader.ReaderActivityLauncher;
 import org.wordpress.android.ui.reader.ReaderFragment;
+import org.wordpress.android.ui.reader.comments.ThreadedCommentsActionSource;
 import org.wordpress.android.ui.reader.services.update.ReaderUpdateLogic.UpdateTask;
 import org.wordpress.android.ui.reader.services.update.ReaderUpdateServiceStarter;
 import org.wordpress.android.ui.reader.tracker.ReaderTracker;
@@ -190,6 +193,9 @@
 import static org.wordpress.android.util.extensions.InAppReviewExtensionsKt.logException;

 import dagger.hilt.android.AndroidEntryPoint;
+import kotlin.Unit;
+import kotlin.jvm.functions.Function0;
+import kotlin.jvm.functions.Function3;

 /**
  * Main activity which hosts sites, reader, me and notifications pages
@@ -251,6 +257,7 @@ public class WPMainActivity extends LocaleAwareActivity implements
     private ModalLayoutPickerViewModel mMLPViewModel;
     @NonNull private ReviewViewModel mReviewViewModel;
     private BloggingRemindersViewModel mBloggingRemindersViewModel;
+    private NotificationsListViewModel mNotificationsViewModel;
     private FloatingActionButton mFloatingActionButton;
     private static final String MAIN_BOTTOM_SHEET_TAG = "MAIN_BOTTOM_SHEET_TAG";
     private static final String BLOGGING_REMINDERS_BOTTOM_SHEET_TAG = "BLOGGING_REMINDERS_BOTTOM_SHEET_TAG";
@@ -1057,11 +1064,34 @@ public void onTokenInvalid() {
                     // we processed the voice reply, so we exit this function immediately
                     return;
                 } else {
-                    boolean shouldShowKeyboard =
-                            getIntent().getBooleanExtra(NotificationsListFragment.NOTE_INSTANT_REPLY_EXTRA, false);
-                    NotificationsListFragment
-                            .openNoteForReply(this, noteId, shouldShowKeyboard, null,
-                                    NotesAdapter.FILTERS.FILTER_ALL, true);
+                    if (mNotificationsViewModel == null) {
+                        mNotificationsViewModel = new ViewModelProvider(this).get(NotificationsListViewModel.class);
+                    }
+                    mNotificationsViewModel.openNote(noteId, new Function3<Long, Long, Long, Unit>() {
+                                @Nullable @Override
+                                public Unit invoke(@NonNull Long siteId, @NonNull Long postId,
+                                                   @NonNull Long commentId) {
+                                    ReaderActivityLauncher.showReaderComments(
+                                            WPMainActivity.this,
+                                            siteId,
+                                            postId,
+                                            commentId,
+                                            ThreadedCommentsActionSource.COMMENT_NOTIFICATION.getSourceDescription()
+                                    );
+                                    return null;
+                                }
+                            }, new Function0<Unit>() {
+                                @Nullable @Override
+                                public Unit invoke() {
+                                    boolean shouldShowKeyboard = getIntent().getBooleanExtra(
+                                            NotificationsListFragment.NOTE_INSTANT_REPLY_EXTRA,
+                                            false);
+                                    NotificationsListFragment.openNoteForReply(WPMainActivity.this, noteId,
+                                            shouldShowKeyboard, null, NotesAdapter.FILTERS.FILTER_ALL, true);
+                                    return null;
+                                }
+                            }
+                    );
                 }
             } else {
                 AppLog.e(T.NOTIFS, "app launched from a PN that doesn't have a note_id in it!!");
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListFragmentPage.kt b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListFragmentPage.kt
index 36d1a996cbde..f578ed92910f 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListFragmentPage.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListFragmentPage.kt
@@ -57,6 +57,8 @@ import org.wordpress.android.ui.notifications.adapters.NotesAdapter.DataLoadedLi
 import org.wordpress.android.ui.notifications.adapters.NotesAdapter.FILTERS
 import org.wordpress.android.ui.notifications.services.NotificationsUpdateServiceStarter
 import org.wordpress.android.ui.notifications.utils.NotificationsActions
+import org.wordpress.android.ui.reader.ReaderActivityLauncher
+import org.wordpress.android.ui.reader.comments.ThreadedCommentsActionSource
 import org.wordpress.android.util.AniUtils
 import org.wordpress.android.util.AppLog
 import org.wordpress.android.util.AppLog.T
@@ -232,9 +234,24 @@ class NotificationsListFragmentPage : ViewPagerFragment(R.layout.notifications_l
             }
             incrementInteractions(APP_REVIEWS_EVENT_INCREMENTED_BY_CHECKING_NOTIFICATION)

-            // Open the latest version of this note in case it has changed, which can happen if the note was tapped
-            // from the list after it was updated by another fragment (such as NotificationsDetailListFragment).
-            openNoteForReply(activity, noteId, false, null, notesAdapter.currentFilter, false)
+            viewModel.openNote(
+                noteId,
+                { siteId, postId, commentId ->
+                    ReaderActivityLauncher.showReaderComments(
+                        activity,
+                        siteId,
+                        postId,
+                        commentId,
+                        ThreadedCommentsActionSource.COMMENT_NOTIFICATION.sourceDescription
+                    )
+                },
+                {
+                    // Open the latest version of this note in case it has changed, which can happen if the note was
+                    // tapped from the list after it was updated by another fragment (such as the
+                    // NotificationsDetailListFragment).
+                    openNoteForReply(activity, noteId, filter = notesAdapter.currentFilter)
+                }
+            )
         }
     }
     private val mOnScrollListener: OnScrollListener = object : OnScrollListener() {
@@ -516,10 +533,10 @@ class NotificationsListFragmentPage : ViewPagerFragment(R.layout.notifications_l
         fun openNoteForReply(
             activity: Activity?,
             noteId: String?,
-            shouldShowKeyboard: Boolean,
-            replyText: String?,
-            filter: FILTERS?,
-            isTappedFromPushNotification: Boolean
+            shouldShowKeyboard: Boolean = false,
+            replyText: String? = null,
+            filter: FILTERS? = null,
+            isTappedFromPushNotification: Boolean = false,
         ) {
             if (noteId == null || activity == null || activity.isFinishing) {
                 return
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListViewModel.kt
index cf2300568044..fae774fb59c4 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListViewModel.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListViewModel.kt
@@ -13,6 +13,7 @@ import org.wordpress.android.fluxc.model.SiteModel
 import org.wordpress.android.fluxc.store.AccountStore
 import org.wordpress.android.fluxc.store.CommentsStore
 import org.wordpress.android.fluxc.store.SiteStore
+import org.wordpress.android.fluxc.utils.AppLogWrapper
 import org.wordpress.android.models.Note
 import org.wordpress.android.models.Notification.PostNotification
 import org.wordpress.android.modules.BG_THREAD
@@ -21,8 +22,11 @@ import org.wordpress.android.ui.jetpackoverlay.JetpackFeatureRemovalOverlayUtil
 import org.wordpress.android.ui.jetpackoverlay.JetpackOverlayConnectedFeature.NOTIFICATIONS
 import org.wordpress.android.ui.notifications.NotificationEvents.NotificationsChanged
 import org.wordpress.android.ui.notifications.utils.NotificationsActions
+import org.wordpress.android.ui.notifications.utils.NotificationsUtilsWrapper
 import org.wordpress.android.ui.prefs.AppPrefsWrapper
+import org.wordpress.android.ui.reader.actions.ReaderActions
 import org.wordpress.android.ui.reader.actions.ReaderPostActionsWrapper
+import org.wordpress.android.util.AppLog
 import org.wordpress.android.viewmodel.Event
 import org.wordpress.android.viewmodel.ScopedViewModel
 import javax.inject.Inject
@@ -34,6 +38,8 @@ class NotificationsListViewModel @Inject constructor(
     private val appPrefsWrapper: AppPrefsWrapper,
     private val jetpackFeatureRemovalOverlayUtil: JetpackFeatureRemovalOverlayUtil,
     private val gcmMessageHandler: GCMMessageHandler,
+    private val notificationsUtilsWrapper: NotificationsUtilsWrapper,
+    private val appLogWrapper: AppLogWrapper,
     private val siteStore: SiteStore,
     private val commentStore: CommentsStore,
     private val readerPostTableWrapper: ReaderPostTableWrapper,
@@ -97,6 +103,36 @@ class NotificationsListViewModel @Inject constructor(
         }
     }

+    fun openNote(
+        noteId: String?,
+        openInTheReader: (siteId: Long, postId: Long, commentId: Long) -> Unit,
+        openDetailView: () -> Unit
+    ) {
+        val note = noteId?.let { notificationsUtilsWrapper.getNoteById(noteId) }
+        if (note != null && note.isCommentType && !note.canModerate()) {
+            val readerPost = readerPostTableWrapper.getBlogPost(note.siteId.toLong(), note.postId.toLong(), false)
+            if (readerPost != null) {
+                openInTheReader(note.siteId.toLong(), note.postId.toLong(), note.commentId)
+            } else {
+                readerPostActionsWrapper.requestBlogPost(
+                    note.siteId.toLong(),
+                    note.postId.toLong(),
+                    object : ReaderActions.OnRequestListener<String> {
+                        override fun onSuccess(result: String?) {
+                            openInTheReader(note.siteId.toLong(), note.postId.toLong(), note.commentId)
+                        }
+
+                        override fun onFailure(statusCode: Int) {
+                            appLogWrapper.w(AppLog.T.NOTIFS, "Failed to fetch post for comment: $statusCode")
+                            openDetailView()
+                        }
+                    })
+            }
+        } else {
+            openDetailView()
+        }
+    }
+
     fun likePost(note: Note, liked: Boolean) = launch {
         note.setLikedPost(liked)
         _updatedNote.postValue(note)

diff --git a/WordPress/src/test/java/org/wordpress/android/ui/notifications/NotificationsListViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/notifications/NotificationsListViewModelTest.kt
new file mode 100644
index 000000000000..ff44cca7c117
--- /dev/null
+++ b/WordPress/src/test/java/org/wordpress/android/ui/notifications/NotificationsListViewModelTest.kt
@@ -0,0 +1,216 @@
+package org.wordpress.android.ui.notifications
+
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnitRunner
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+import org.wordpress.android.BaseUnitTest
+import org.wordpress.android.datasets.wrappers.ReaderPostTableWrapper
+import org.wordpress.android.fluxc.store.AccountStore
+import org.wordpress.android.fluxc.store.CommentsStore
+import org.wordpress.android.fluxc.store.SiteStore
+import org.wordpress.android.fluxc.utils.AppLogWrapper
+import org.wordpress.android.models.Note
+import org.wordpress.android.models.ReaderPost
+import org.wordpress.android.push.GCMMessageHandler
+import org.wordpress.android.ui.jetpackoverlay.JetpackFeatureRemovalOverlayUtil
+import org.wordpress.android.ui.notifications.utils.NotificationsUtilsWrapper
+import org.wordpress.android.ui.prefs.AppPrefsWrapper
+import org.wordpress.android.ui.reader.actions.ReaderActions
+import org.wordpress.android.ui.reader.actions.ReaderPostActionsWrapper
+
+private const val REQUEST_BLOG_LISTENER_PARAM_POSITION = 2
+
+@ExperimentalCoroutinesApi
+@RunWith(MockitoJUnitRunner::class)
+class NotificationsListViewModelTest : BaseUnitTest() {
+    @Mock
+    private lateinit var appPrefsWrapper: AppPrefsWrapper
+
+    @Mock
+    private lateinit var jetpackFeatureRemovalOverlayUtil: JetpackFeatureRemovalOverlayUtil
+
+    @Mock
+    private lateinit var gcmMessageHandler: GCMMessageHandler
+
+    @Mock
+    private lateinit var notificationsUtilsWrapper: NotificationsUtilsWrapper
+
+    @Mock
+    private lateinit var readerPostTableWrapper: ReaderPostTableWrapper
+
+    @Mock
+    private lateinit var readerPostActionsWrapper: ReaderPostActionsWrapper
+
+    @Mock lateinit var appLogWrapper: AppLogWrapper
+
+    @Mock
+    private lateinit var siteStore: SiteStore
+
+    @Mock
+    private lateinit var commentStore: CommentsStore
+
+    @Mock
+    private lateinit var  accountStore: AccountStore
+
+    @Mock
+    private lateinit var action: ActionHandler
+
+    private lateinit var viewModel: NotificationsListViewModel
+
+    @Before
+    fun setup() {
+        viewModel = NotificationsListViewModel(
+            testDispatcher(),
+            appPrefsWrapper,
+            jetpackFeatureRemovalOverlayUtil,
+            gcmMessageHandler,
+            notificationsUtilsWrapper,
+            appLogWrapper,
+            siteStore,
+            commentStore,
+            readerPostTableWrapper,
+            readerPostActionsWrapper,
+            accountStore,
+        )
+    }
+
+    @Test
+    fun `WHEN the note cannot be retrieved THEN try opening the detail view`() {
+        // Given
+        val noteId = "1"
+        whenever(notificationsUtilsWrapper.getNoteById(noteId)).thenReturn(null)
+
+        // When
+        viewModel.openNote(noteId, action::openInTheReader, action::openDetailView)
+
+        // Then
+        verify(action, times(0)).openInTheReader(any(), any(), any())
+        verify(action, times(1)).openDetailView()
+    }
+
+    @Test
+    fun `WHEN the note is not a comment THEN open detail view`() {
+        // Given
+        val noteId = "1"
+        val note = mock<Note>()
+        whenever(notificationsUtilsWrapper.getNoteById(noteId)).thenReturn(note)
+        whenever(note.isCommentType).thenReturn(false)
+
+        // When
+        viewModel.openNote(noteId, action::openInTheReader, action::openDetailView)
+
+        // Then
+        verify(action, times(0)).openInTheReader(any(), any(), any())
+        verify(action, times(1)).openDetailView()
+    }
+
+    @Test
+    fun `WHEN the note is a comment that can be moderated THEN open detail view`() {
+        // Given
+        val noteId = "1"
+        val note = mock<Note>()
+        whenever(notificationsUtilsWrapper.getNoteById(noteId)).thenReturn(note)
+        whenever(note.isCommentType).thenReturn(true)
+        whenever(note.canModerate()).thenReturn(true)
+
+        // When
+        viewModel.openNote(noteId, action::openInTheReader, action::openDetailView)
+
+        // Then
+        verify(action, times(0)).openInTheReader(any(), any(), any())
+        verify(action, times(1)).openDetailView()
+    }
+
+    @Test
+    fun `WHEN the note is a comment that cannot be moderated and the reader post exists THEN open in reader`() {
+        // Given
+        val noteId = "1"
+        val siteId = 1L
+        val postId = 2L
+        val commentId = 3L
+        val note = mock<Note>()
+        val readerPost = mock<ReaderPost>()
+        whenever(notificationsUtilsWrapper.getNoteById(noteId)).thenReturn(note)
+        whenever(note.siteId).thenReturn(siteId.toInt())
+        whenever(note.postId).thenReturn(postId.toInt())
+        whenever(note.commentId).thenReturn(commentId)
+        whenever(note.isCommentType).thenReturn(true)
+        whenever(note.canModerate()).thenReturn(false)
+        whenever(readerPostTableWrapper.getBlogPost(siteId, postId, false)).thenReturn(readerPost)
+
+        // When
+        viewModel.openNote(noteId, action::openInTheReader, action::openDetailView)
+
+        // Then
+        verify(action, times(1)).openInTheReader(siteId, postId, commentId)
+        verify(action, times(0)).openDetailView()
+    }
+
+    @Test
+    fun `WHEN the note is a comment that cannot be moderated and the reader post is retrieved THEN open in reader`() {
+        // Given
+        val noteId = "1"
+        val siteId = 1L
+        val postId = 2L
+        val commentId = 3L
+        val note = mock<Note>()
+        whenever(notificationsUtilsWrapper.getNoteById(noteId)).thenReturn(note)
+        whenever(note.siteId).thenReturn(siteId.toInt())
+        whenever(note.postId).thenReturn(postId.toInt())
+        whenever(note.commentId).thenReturn(commentId)
+        whenever(note.isCommentType).thenReturn(true)
+        whenever(note.canModerate()).thenReturn(false)
+        whenever(readerPostTableWrapper.getBlogPost(siteId, postId, false)).thenReturn(null)
+        whenever(readerPostActionsWrapper.requestBlogPost(any(), any(), any())).then {
+            (it.arguments[REQUEST_BLOG_LISTENER_PARAM_POSITION] as ReaderActions.OnRequestListener<*>)
+                .onSuccess(null)
+        }
+
+        // When
+        viewModel.openNote(noteId, action::openInTheReader, action::openDetailView)
+
+        // Then
+        verify(action, times(1)).openInTheReader(siteId, postId, commentId)
+        verify(action, times(0)).openDetailView()
+    }
+
+    @Test
+    fun `WHEN the comment note cannot be moderated and the reader post retrieval fails THEN open detail view`() {
+        // Given
+        val noteId = "1"
+        val siteId = 1L
+        val postId = 2L
+        val note = mock<Note>()
+        whenever(notificationsUtilsWrapper.getNoteById(noteId)).thenReturn(note)
+        whenever(note.siteId).thenReturn(siteId.toInt())
+        whenever(note.postId).thenReturn(postId.toInt())
+        whenever(note.isCommentType).thenReturn(true)
+        whenever(note.canModerate()).thenReturn(false)
+        whenever(readerPostTableWrapper.getBlogPost(siteId, postId, false)).thenReturn(null)
+        whenever(readerPostActionsWrapper.requestBlogPost(any(), any(), any())).then {
+            (it.arguments[REQUEST_BLOG_LISTENER_PARAM_POSITION] as ReaderActions.OnRequestListener<*>)
+                .onFailure(500)
+        }
+
+        // When
+        viewModel.openNote(noteId, action::openInTheReader, action::openDetailView)
+
+        // Then
+        verify(action, times(0)).openInTheReader(any(), any(), any())
+        verify(action, times(1)).openDetailView()
+    }
+
+    interface ActionHandler {
+        fun openInTheReader(siteId: Long, postId: Long, commentId: Long)
+
+        fun openDetailView()
+    }
+}


Fixes #20099

## Description
This PR adds the whole content of a comment type push notification (as it is passed by the service).
It also changes the comment notification title aligning with iOS.

|Before Android|Current iOS|
|---|---|
|||

|After Expanded|After Lockscreen/Collapsed|
|---|---|
|||

-----

## To Test:
#### Note: You can use the CI build for testing or a build signed with the production keys
### Comment notification
1. Send a comment notification to yourself either via another account or by using a tool (see pcdRpT-5yH-p2#comment-8854)
2. **Verify** that the compact view of the notification is the expected (e.g. in the lockscreen)
3. **Verify** that the expanded notification contains the first sentences of the comment
#### Note: The comment might be truncated at the end due to the service limitations. An improvement on this would be investigated on the backend.
### Non-Comment notification (follow, like, comment like,...)
1. Send a comment notification to yourself either via another account or by using a tool (see pcdRpT-5yH-p2#comment-8854)
2. **Verify** that the compact view of the notification hasn't changed
3. **Verify** that the expanded notification hasn't changed

-----

## Regression Notes

1. Potential unintended areas of impact

    - PN functionality

5. What I did to test those areas of impact (or what existing automated tests I relied on)

    - Manual testing

6. What automated tests I added (or what prevented me from doing so)

    - Added test cases for the PN title and message selection logic

-----

## 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:

- [ ] 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)

🤖 Add the content of a comment in the push notification
The push notification should include the content of the comment (like, comment). When the content does not fit [the notification should expand](https://developer.android.com/develop/ui/views/notifications#expandable).

./gradlew :WordPress:testWordpressVanillaDebugUnitTest --tests "org.wordpress.android.push.NotificationHelperTest"

diff --git a/WordPress/src/main/java/org/wordpress/android/push/GCMMessageHandler.java b/WordPress/src/main/java/org/wordpress/android/push/GCMMessageHandler.java
index 5495ae79f441..1ecc1ad4ff77 100644
--- a/WordPress/src/main/java/org/wordpress/android/push/GCMMessageHandler.java
+++ b/WordPress/src/main/java/org/wordpress/android/push/GCMMessageHandler.java
@@ -32,6 +32,7 @@
 import org.wordpress.android.ui.notifications.SystemNotificationsTracker;
 import org.wordpress.android.ui.notifications.utils.NotificationsActions;
 import org.wordpress.android.ui.notifications.utils.NotificationsUtils;
+import org.wordpress.android.ui.notifications.utils.NotificationsUtilsWrapper;
 import org.wordpress.android.ui.prefs.AppPrefs;
 import org.wordpress.android.util.AppLog;
 import org.wordpress.android.util.AppLog.T;
@@ -74,10 +75,10 @@ public class GCMMessageHandler {

     private static final String PUSH_ARG_TYPE = "type";
     private static final String PUSH_ARG_USER = "user";
-    private static final String PUSH_ARG_TITLE = "title";
-    private static final String PUSH_ARG_MSG = "msg";
+    protected static final String PUSH_ARG_TITLE = "title";
+    protected static final String PUSH_ARG_MSG = "msg";

-    private static final String PUSH_TYPE_COMMENT = "c";
+    protected static final String PUSH_TYPE_COMMENT = "c";
     private static final String PUSH_TYPE_LIKE = "like";
     private static final String PUSH_TYPE_COMMENT_LIKE = "comment_like";
     private static final String PUSH_TYPE_AUTOMATTCHER = "automattcher";
@@ -100,9 +101,10 @@ public class GCMMessageHandler {
     private final ArrayMap<Integer, Bundle> mActiveNotificationsMap;
     private final NotificationHelper mNotificationHelper;

-    @Inject GCMMessageHandler(SystemNotificationsTracker systemNotificationsTracker) {
+    @Inject GCMMessageHandler(SystemNotificationsTracker systemNotificationsTracker,
+                              NotificationsUtilsWrapper notificationsUtilsWrapper) {
         mActiveNotificationsMap = new ArrayMap<>();
-        mNotificationHelper = new NotificationHelper(this, systemNotificationsTracker);
+        mNotificationHelper = new NotificationHelper(this, systemNotificationsTracker, notificationsUtilsWrapper);
     }

     synchronized void rebuildAndUpdateNotificationsOnSystemBarForThisNote(Context context,
@@ -120,13 +122,6 @@ synchronized void rebuildAndUpdateNotificationsOnSystemBarForThisNote(Context co
         }
     }

-    public synchronized void rebuildAndUpdateNotifsOnSystemBarForRemainingNote(Context context) {
-        if (mActiveNotificationsMap.size() > 0) {
-            Bundle remainingNote = mActiveNotificationsMap.values().iterator().next();
-            mNotificationHelper.rebuildAndUpdateNotificationsOnSystemBar(context, remainingNote);
-        }
-    }
-
     private synchronized Bundle getCurrentNoteBundleForNoteId(String noteId) {
         if (mActiveNotificationsMap.size() > 0) {
             // get the corresponding bundle for this noteId
@@ -278,10 +273,14 @@ public static class NotificationHelper {
         private GCMMessageHandler mGCMMessageHandler;
         private SystemNotificationsTracker mSystemNotificationsTracker;

+        private NotificationsUtilsWrapper mNotificationsUtilsWrapper;
+
         NotificationHelper(GCMMessageHandler gCMMessageHandler,
-                           SystemNotificationsTracker systemNotificationsTracker) {
+                           SystemNotificationsTracker systemNotificationsTracker,
+                           NotificationsUtilsWrapper notificationsUtilsWrapper) {
             mGCMMessageHandler = gCMMessageHandler;
             mSystemNotificationsTracker = systemNotificationsTracker;
+            mNotificationsUtilsWrapper = notificationsUtilsWrapper;
         }

         void handleDefaultPush(Context context, @NonNull Bundle data, long wpcomUserId) {
@@ -354,11 +353,8 @@ private void buildAndShowNotificationFromNoteData(Context context, Bundle data)

             String noteType = StringUtils.notNullStr(data.getString(PUSH_ARG_TYPE));

-            String title = StringEscapeUtils.unescapeHtml4(data.getString(PUSH_ARG_TITLE));
-            if (title == null) {
-                title = context.getString(R.string.app_name);
-            }
-            String message = StringEscapeUtils.unescapeHtml4(data.getString(PUSH_ARG_MSG));
+            String title = getNotificationTitle(data, noteType, context.getString(R.string.app_name));
+            String message = getNotificationMessage(data, noteType);

             /*
              * if this has the same note_id as the previous notification, and the previous notification
@@ -421,6 +417,45 @@ private void buildAndShowNotificationFromNoteData(Context context, Bundle data)
             );
         }

+        @NonNull
+        protected String getNotificationTitle(@NonNull Bundle data,
+                                              @NonNull String noteType,
+                                              @NonNull String defaultTitle) {
+            String title;
+            if (noteType.equals(PUSH_TYPE_COMMENT)) {
+                title = StringEscapeUtils.unescapeHtml4(data.getString(PUSH_ARG_MSG));
+            } else {
+                title = StringEscapeUtils.unescapeHtml4(data.getString(PUSH_ARG_TITLE));
+            }
+            if (title == null) {
+                return defaultTitle;
+            }
+            return title;
+        }
+
+        @NonNull
+        protected String getNotificationMessage(@NonNull Bundle data, @NonNull String noteType) {
+            if (noteType.equals(PUSH_TYPE_COMMENT)) {
+                String noteId = data.getString(PUSH_ARG_NOTE_ID);
+                if (noteId != null) {
+                    Note note = mNotificationsUtilsWrapper.getNoteById(noteId);
+                    if (note != null) {
+                        String summary = note.getCommentSubject();
+                        if (!TextUtils.isEmpty(summary)) {
+                            return summary;
+                        }
+                    }
+                }
+            }
+
+            // Not a comment or the comment content was not retrieved
+            String message = StringEscapeUtils.unescapeHtml4(data.getString(PUSH_ARG_MSG));
+            if (message == null) {
+                return "";
+            }
+            return message;
+        }
+
         private void showNotificationForNoteData(Context context, Bundle noteData, NotificationCompat.Builder builder) {
             String noteType = StringUtils.notNullStr(noteData.getString(PUSH_ARG_TYPE));
             String wpcomNoteID = noteData.getString(PUSH_ARG_NOTE_ID, "");
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/notifications/utils/NotificationsUtils.java b/WordPress/src/main/java/org/wordpress/android/ui/notifications/utils/NotificationsUtils.java
index 2d8ad8c77485..151916d79294 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/notifications/utils/NotificationsUtils.java
+++ b/WordPress/src/main/java/org/wordpress/android/ui/notifications/utils/NotificationsUtils.java
@@ -17,6 +17,7 @@
 import android.view.View;
 import android.widget.TextView;

+import androidx.annotation.Nullable;
 import androidx.appcompat.app.AlertDialog;
 import androidx.core.app.NotificationManagerCompat;
 import androidx.preference.PreferenceManager;
@@ -469,6 +470,11 @@ public static boolean buildNoteObjectFromBundleAndSaveIt(Bundle data) {
         return false;
     }

+    @Nullable
+    public static Note getNoteById(@Nullable String noteID) {
+        return NotificationsTable.getNoteById(noteID);
+    }
+
     public static Note buildNoteObjectFromBundle(Bundle data) {
         if (data == null) {
             AppLog.e(T.NOTIFS, "Bundle is null! Cannot read '" + GCMMessageService.PUSH_ARG_NOTE_ID + "'.");
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/notifications/utils/NotificationsUtilsWrapper.kt b/WordPress/src/main/java/org/wordpress/android/ui/notifications/utils/NotificationsUtilsWrapper.kt
index b2ccbf49a396..bef354abe943 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/notifications/utils/NotificationsUtilsWrapper.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/notifications/utils/NotificationsUtilsWrapper.kt
@@ -6,6 +6,7 @@ import org.json.JSONObject
 import org.wordpress.android.fluxc.tools.FormattableContent
 import org.wordpress.android.fluxc.tools.FormattableContentMapper
 import org.wordpress.android.fluxc.tools.FormattableRange
+import org.wordpress.android.models.Note
 import org.wordpress.android.ui.notifications.blocks.NoteBlock
 import javax.inject.Inject
 import javax.inject.Singleton
@@ -69,4 +70,6 @@ class NotificationsUtilsWrapper @Inject constructor(private val formattableConte

     fun mapJsonToFormattableContent(blockObject: JSONObject): FormattableContent = NotificationsUtils
         .mapJsonToFormattableContent(formattableContentMapper, blockObject)
+
+    fun getNoteById(noteId: String): Note? = NotificationsUtils.getNoteById(noteId)
 }

diff --git a/WordPress/src/test/java/org/wordpress/android/push/NotificationHelperTest.kt b/WordPress/src/test/java/org/wordpress/android/push/NotificationHelperTest.kt
new file mode 100644
index 000000000000..77430434c066
--- /dev/null
+++ b/WordPress/src/test/java/org/wordpress/android/push/NotificationHelperTest.kt
@@ -0,0 +1,114 @@
+package org.wordpress.android.push
+
+import android.os.Bundle
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import org.apache.commons.text.StringEscapeUtils
+import org.junit.Test
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+import org.wordpress.android.BaseUnitTest
+import org.wordpress.android.models.Note
+import org.wordpress.android.push.GCMMessageHandler.PUSH_TYPE_COMMENT
+import org.wordpress.android.push.GCMMessageHandler.PUSH_ARG_MSG
+import org.wordpress.android.push.GCMMessageHandler.PUSH_ARG_TITLE
+import org.wordpress.android.push.GCMMessageService.PUSH_ARG_NOTE_ID
+import org.wordpress.android.ui.notifications.SystemNotificationsTracker
+import org.wordpress.android.ui.notifications.utils.NotificationsUtilsWrapper
+import kotlin.test.assertEquals
+
+private const val PUSH_TYPE_NOT_A_COMMENT = "NotAComment"
+
+@ExperimentalCoroutinesApi
+class NotificationHelperTest : BaseUnitTest() {
+    private val systemNotificationsTracker: SystemNotificationsTracker = mock()
+    private val gcmMessageHandler: GCMMessageHandler = mock()
+    private val notificationsUtilsWrapper = mock<NotificationsUtilsWrapper>()
+    private val notificationHelper =
+        GCMMessageHandler.NotificationHelper(gcmMessageHandler, systemNotificationsTracker, notificationsUtilsWrapper)
+
+    @Test
+    fun `WHEN a PN that is a comment has a message argument THEN then the message is used as title`() {
+        val expectedTitle = "expectedTitle"
+        val defaultTitle = "defaultTitle"
+        val mockedBundle = mock<Bundle>()
+        whenever(mockedBundle.getString(PUSH_ARG_MSG)).thenReturn(StringEscapeUtils.escapeHtml4(expectedTitle))
+        val title = notificationHelper.getNotificationTitle(mockedBundle, PUSH_TYPE_COMMENT, defaultTitle)
+        assertEquals(expectedTitle, title)
+    }
+
+    @Test
+    fun `WHEN a PN that is not a comment contains a title argument THEN this title is used`() {
+        val expectedTitle = "expectedTitle"
+        val defaultTitle = "defaultTitle"
+        val mockedBundle = mock<Bundle>()
+        whenever(mockedBundle.getString(PUSH_ARG_TITLE)).thenReturn(StringEscapeUtils.escapeHtml4(expectedTitle))
+        val title = notificationHelper.getNotificationTitle(mockedBundle, PUSH_TYPE_NOT_A_COMMENT, defaultTitle)
+        assertEquals(expectedTitle, title)
+    }
+
+    @Test
+    fun `WHEN a PN that is not a comment does not contain a title argument THEN the default title is used`() {
+        val defaultTitle = "defaultTitle"
+        val mockedBundle = mock<Bundle>()
+        whenever(mockedBundle.getString(PUSH_ARG_TITLE)).thenReturn(null)
+        val title = notificationHelper.getNotificationTitle(mockedBundle, PUSH_TYPE_NOT_A_COMMENT, defaultTitle)
+        assertEquals(defaultTitle, title)
+    }
+
+    @Test
+    fun `WHEN a PN that is not a comment contains a message argument THEN the message is used`() {
+        val expectedMessage = "expectedMessage"
+        val mockedBundle = mock<Bundle>()
+        whenever(mockedBundle.getString(PUSH_ARG_MSG)).thenReturn(StringEscapeUtils.escapeHtml4(expectedMessage))
+        val message = notificationHelper.getNotificationMessage(mockedBundle, PUSH_TYPE_NOT_A_COMMENT)
+        assertEquals(expectedMessage, message)
+    }
+
+    @Test
+    fun `WHEN a PN that is not a comment does not contain a message argument THEN an empty message is used`() {
+        val mockedBundle = mock<Bundle>()
+        whenever(mockedBundle.getString(PUSH_ARG_MSG)).thenReturn(null)
+        val message = notificationHelper.getNotificationMessage(mockedBundle, PUSH_TYPE_NOT_A_COMMENT)
+        assertEquals("", message)
+    }
+
+    @Test
+    fun `WHEN a PN that is a comment has a comment payload THEN the comment is used as a message`() {
+        val expectedMessage = "expectedMessage"
+        val mockedNote = mock<Note>()
+        val noteId = "noteId"
+        val mockedBundle = mock<Bundle>()
+        whenever(mockedBundle.getString(PUSH_ARG_NOTE_ID)).thenReturn(noteId)
+        whenever(notificationsUtilsWrapper.getNoteById(noteId)).thenReturn(mockedNote)
+        whenever(mockedNote.commentSubject).thenReturn(expectedMessage)
+        val message = notificationHelper.getNotificationMessage(mockedBundle, PUSH_TYPE_COMMENT)
+        assertEquals(expectedMessage, message)
+    }
+
+    @Test
+    fun `WHEN a PN that is a comment does not have a comment payload and contains a message THEN the later is used`() {
+        val expectedMessage = "expectedMessage"
+        val mockedNote = mock<Note>()
+        val noteId = "noteId"
+        val mockedBundle = mock<Bundle>()
+        whenever(mockedBundle.getString(PUSH_ARG_NOTE_ID)).thenReturn(noteId)
+        whenever(notificationsUtilsWrapper.getNoteById(noteId)).thenReturn(mockedNote)
+        whenever(mockedNote.commentSubject).thenReturn(null)
+        whenever(mockedBundle.getString(PUSH_ARG_MSG)).thenReturn(StringEscapeUtils.escapeHtml4(expectedMessage))
+        val message = notificationHelper.getNotificationMessage(mockedBundle, PUSH_TYPE_COMMENT)
+        assertEquals(expectedMessage, message)
+    }
+
+    @Test
+    fun `WHEN a PN that is a comment does not have a comment payload or a message THEN an empty message is used`() {
+        val mockedNote = mock<Note>()
+        val noteId = "noteId"
+        val mockedBundle = mock<Bundle>()
+        whenever(mockedBundle.getString(PUSH_ARG_NOTE_ID)).thenReturn(noteId)
+        whenever(notificationsUtilsWrapper.getNoteById(noteId)).thenReturn(mockedNote)
+        whenever(mockedNote.commentSubject).thenReturn(null)
+        whenever(mockedBundle.getString(PUSH_ARG_MSG)).thenReturn(null)
+        val message = notificationHelper.getNotificationMessage(mockedBundle, PUSH_TYPE_COMMENT)
+        assertEquals("", message)
+    }
+}


[Reader IA] Simplify Reader Quick Start flow for new UX

Fixes #20109

- Remove unused code relative to the old top app bar and menu
- Update the Quick Start flow to auto-complete upon showing Snackbar message
- Update snackbar message for WP.com users to mention Subscriptions feed instead of Settings
- No longer try to go to Discover feed when doing the quick start flow

New message:
> Use **Discover** to find sites and tags. Try selecting **Subscriptions** to view subscribed content and manage your subscriptions.

https://github.com/wordpress-mobile/WordPress-Android/assets/5091503/9094539b-c37d-47f5-a932-d897aaa2f804

-----

## To Test:
Make sure you're not already logged in.

1. Install and log into Jetpack
2. Tap the option to go through onboarding, right after login
3. Tap the onboarding card on My Site Dashboard ("Get to know the app")
4. Tap "Connect with other sites"
5. **Verify** the Reader icon is updated in the first snack bar message
6. **Verify** tapping Reader shows a snackbar with the new message
7. Go back to My Site
8. **Verify** the onboarding task is considered completed

You can also go through the steps in #20109 and **verify** it no longer happens since we don't try to move to Discover anymore and just stay on the selected Feed.

-----

## Regression Notes

1. Potential unintended areas of impact

    - Quick start flow issues

9. What I did to test those areas of impact (or what existing automated tests I relied on)

    - Manual testing, unit testing

10. What automated tests I added (or what prevented me from doing so)

    - Updated existing tests
    - Added tests for the new scenarios

-----

## 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:

- [ ] 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)

[Reader IA][Bug] Inconsistent reader UI state under circumstances going over quickstart
Internal ref: p1706830673644549?thread_ts=1706821601.141289&cid=C01CW1VMLAF-slack-C01CW1VMLAF

### Expected behavior
When Subscriptions is selected and we go into the reader quickstart step, the Discover feed is autoselected by the quickstart step and the Subscriptions blogs and sites should be not present in the UI

### Actual behavior
When Subscriptions is selected and we go into the reader quickstart step, the Discover feed is autoselected by the quickstart step but the Subscriptions blogs and sites are not removed (and do not respond to taps anyways)

### Steps to reproduce the behavior
- Select Subscriptions in the reader drop down menu
- Go into the Quickstart "Connect with other sites" step

<img width="300" alt="image" src="https://github.com/wordpress-mobile/WordPress-Android/assets/47797566/956b80d7-47ba-44f7-bae6-c4c30d1274a5">

- Go into the reader
- Notice the Discover feed is correctly selected but the blogs and tags pills are not removed from the UI

https://github.com/wordpress-mobile/WordPress-Android/assets/47797566/8e89e2db-f1c4-483d-9f8a-2154f1c62cd6

##### Tested on [S23], Android [Android 14], JPAndroid / WPAndroid [#20103 at 2ce97ec71e2b3317ed2bc3c115c32647f2eb759d]

diff --git a/WordPress/src/main/java/org/wordpress/android/ui/quickstart/QuickStartMySitePrompts.kt b/WordPress/src/main/java/org/wordpress/android/ui/quickstart/QuickStartMySitePrompts.kt
index 5861be66f0f4..4d200bdbfb34 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/quickstart/QuickStartMySitePrompts.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/quickstart/QuickStartMySitePrompts.kt
@@ -50,7 +50,7 @@ enum class QuickStartMySitePrompts constructor(
         R.id.root_view_main,
         R.id.bottom_nav_reader_button,
         R.string.quick_start_dialog_follow_sites_message_short_reader,
-        R.drawable.ic_reader_white_24dp
+        R.drawable.ic_reader_selected
     ),
     UPLOAD_SITE_ICON(
         QuickStartStore.QUICK_START_UPLOAD_SITE_ICON_LABEL,
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderFragment.kt
index 49bc8607a84c..28397a28fb70 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderFragment.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderFragment.kt
@@ -1,14 +1,10 @@
 package org.wordpress.android.ui.reader

 import android.os.Bundle
-import android.view.Menu
-import android.view.MenuInflater
-import android.view.MenuItem
 import android.view.View
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.livedata.observeAsState
 import androidx.compose.ui.platform.ViewCompositionStrategy
-import androidx.core.view.MenuProvider
 import androidx.core.view.isVisible
 import androidx.fragment.app.Fragment
 import androidx.lifecycle.ViewModelProvider
@@ -48,12 +44,11 @@ import org.wordpress.android.util.SnackbarItem.Action
 import org.wordpress.android.util.SnackbarItem.Info
 import org.wordpress.android.util.SnackbarSequencer
 import org.wordpress.android.viewmodel.observeEvent
-import org.wordpress.android.widgets.QuickStartFocusPoint
 import java.util.EnumSet
 import javax.inject.Inject

 @AndroidEntryPoint
-class ReaderFragment : Fragment(R.layout.reader_fragment_layout), MenuProvider, ScrollableViewInitializedListener {
+class ReaderFragment : Fragment(R.layout.reader_fragment_layout), ScrollableViewInitializedListener {
     @Inject
     lateinit var viewModelFactory: ViewModelProvider.Factory

@@ -70,14 +65,9 @@ class ReaderFragment : Fragment(R.layout.reader_fragment_layout), MenuProvider,
     lateinit var snackbarSequencer: SnackbarSequencer
     private lateinit var viewModel: ReaderViewModel

-    private var searchMenuItem: MenuItem? = null
-    private var settingsMenuItem: MenuItem? = null
-    private var settingsMenuItemFocusPoint: QuickStartFocusPoint? = null
-
     private var binding: ReaderFragmentLayoutBinding? = null

     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
-        requireActivity().addMenuProvider(this, viewLifecycleOwner)
         binding = ReaderFragmentLayoutBinding.bind(view).apply {
             initTopAppBar()
             initViewModel(savedInstanceState)
@@ -86,9 +76,6 @@ class ReaderFragment : Fragment(R.layout.reader_fragment_layout), MenuProvider,

     override fun onDestroyView() {
         super.onDestroyView()
-        searchMenuItem = null
-        settingsMenuItem = null
-        settingsMenuItemFocusPoint = null
         binding = null
     }

@@ -102,36 +89,6 @@ class ReaderFragment : Fragment(R.layout.reader_fragment_layout), MenuProvider,
         activity?.let { viewModel.onScreenInBackground(it.isChangingConfigurations) }
     }

-    override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
-        menuInflater.inflate(R.menu.reader_home, menu)
-        menu.findItem(R.id.menu_search).apply {
-            searchMenuItem = this
-            this.isVisible = viewModel.uiState.value?.searchMenuItemUiState?.isVisible ?: false
-        }
-        menu.findItem(R.id.menu_settings).apply {
-            settingsMenuItem = this
-            settingsMenuItemFocusPoint = this.actionView?.findViewById(R.id.menu_quick_start_focus_point)
-            this.isVisible = viewModel.uiState.value?.settingsMenuItemUiState?.isVisible ?: false
-            settingsMenuItemFocusPoint?.isVisible =
-                viewModel.uiState.value?.settingsMenuItemUiState?.showQuickStartFocusPoint ?: false
-            this.actionView?.setOnClickListener { viewModel.onSettingsActionClicked() }
-        }
-    }
-
-    override fun onMenuItemSelected(menuItem: MenuItem) = when (menuItem.itemId) {
-        R.id.menu_search -> {
-            viewModel.onSearchActionClicked()
-            true
-        }
-
-        R.id.menu_settings -> {
-            viewModel.onSettingsActionClicked()
-            true
-        }
-
-        else -> false
-    }
-
     private fun ReaderFragmentLayoutBinding.initTopAppBar() {
         readerTopBarComposeView.apply {
             setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
@@ -171,10 +128,6 @@ class ReaderFragment : Fragment(R.layout.reader_fragment_layout), MenuProvider,
             ReaderActivityLauncher.showReaderSearch(context)
         }

-        viewModel.showSettings.observeEvent(viewLifecycleOwner) {
-            ReaderActivityLauncher.showReaderSubs(context)
-        }
-
         viewModel.showReaderInterests.observeEvent(viewLifecycleOwner) {
             showReaderInterests()
         }
@@ -225,12 +178,6 @@ class ReaderFragment : Fragment(R.layout.reader_fragment_layout), MenuProvider,
                 initContentContainer(uiState)
             }
         }
-        // TODO As part of Reader IA changes this view is going to be replaced
-        //                uiHelpers.updateVisibility(tabLayout, uiState.tabLayoutVisible)
-        searchMenuItem?.isVisible = uiState.searchMenuItemUiState.isVisible
-        settingsMenuItem?.isVisible = uiState.settingsMenuItemUiState.isVisible
-        settingsMenuItemFocusPoint?.isVisible =
-            viewModel.uiState.value?.settingsMenuItemUiState?.showQuickStartFocusPoint ?: false
     }

     private fun initContentContainer(uiState: ContentUiState) {
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/discover/interests/ReaderInterestsViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/reader/discover/interests/ReaderInterestsViewModel.kt
index 67be5e9cd0ec..b2a754a06137 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/discover/interests/ReaderInterestsViewModel.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/discover/interests/ReaderInterestsViewModel.kt
@@ -168,8 +168,6 @@ class ReaderInterestsViewModel @Inject constructor(
                     )
                 )
             )
-
-            parentViewModel?.completeQuickStartFollowSiteTaskIfNeeded()
         }
     }

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 2f97cf4fcbea..e78ad354d41e 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
@@ -42,13 +42,13 @@ import org.wordpress.android.ui.reader.usecases.LoadReaderTabsUseCase
 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
-import org.wordpress.android.ui.reader.viewmodels.ReaderViewModel.ReaderUiState.ContentUiState.MenuItemUiState
 import org.wordpress.android.ui.reader.viewmodels.ReaderViewModel.ReaderUiState.ContentUiState.TabUiState
 import org.wordpress.android.ui.reader.views.compose.filter.ReaderFilterSelectedItem
 import org.wordpress.android.ui.reader.views.compose.filter.ReaderFilterType
 import org.wordpress.android.ui.utils.UiString
 import org.wordpress.android.ui.utils.UiString.UiStringText
 import org.wordpress.android.util.JetpackBrandingUtils
+import org.wordpress.android.util.QuickStartUtils
 import org.wordpress.android.util.SnackbarSequencer
 import org.wordpress.android.util.distinct
 import org.wordpress.android.viewmodel.Event
@@ -94,9 +94,6 @@ class ReaderViewModel @Inject constructor(
     private val _showSearch = MutableLiveData<Event<Unit>>()
     val showSearch: LiveData<Event<Unit>> = _showSearch

-    private val _showSettings = MutableLiveData<Event<Unit>>()
-    val showSettings: LiveData<Event<Unit>> = _showSettings
-
     private val _showReaderInterests = MutableLiveData<Event<Unit>>()
     val showReaderInterests: LiveData<Event<Unit>> = _showReaderInterests

@@ -138,7 +135,6 @@ class ReaderViewModel @Inject constructor(

     private fun loadTabs(savedInstanceState: Bundle? = null) {
         launch {
-            val currentContentUiState = _uiState.value as? ContentUiState
             val tagList = loadReaderTabsUseCase.loadTabs()
             if (tagList.isNotEmpty() && readerTagsList != tagList) {
                 updateReaderTagsList(tagList)
@@ -146,12 +142,6 @@ class ReaderViewModel @Inject constructor(
                 _uiState.value = ContentUiState(
                     tabUiStates = tagList.map { TabUiState(label = UiStringText(it.label)) },
                     selectedReaderTag = selectedReaderTag(),
-                    searchMenuItemUiState = MenuItemUiState(isVisible = isSearchSupported()),
-                    settingsMenuItemUiState = MenuItemUiState(
-                        isVisible = isSettingsSupported(),
-                        showQuickStartFocusPoint =
-                        currentContentUiState?.settingsMenuItemUiState?.showQuickStartFocusPoint ?: false
-                    )
                 )
                 if (!initialized) {
                     initialized = true
@@ -221,16 +211,6 @@ class ReaderViewModel @Inject constructor(
         }
     }

-    @Suppress("UseCheckOrError")
-    fun onSettingsActionClicked() {
-        if (isSettingsSupported()) {
-            completeQuickStartFollowSiteTaskIfNeeded()
-            _showSettings.value = Event(Unit)
-        } else if (BuildConfig.DEBUG) {
-            throw IllegalStateException("Settings should be hidden when isSettingsSupported returns false.")
-        }
-    }
-
     @Suppress("unused", "UNUSED_PARAMETER")
     @Subscribe(threadMode = MAIN)
     fun onTagsUpdated(event: ReaderEvents.FollowedTagsChanged) {
@@ -253,7 +233,6 @@ class ReaderViewModel @Inject constructor(
         readerTracker.stop(MAIN_READER)
         wasPaused = true
         if (!isChangingConfigurations) {
-            hideQuickStartFocusPointIfNeeded()
             dismissQuickStartSnackbarIfNeeded()
             if (quickStartRepository.isPendingTask(getFollowSiteTask())) {
                 quickStartRepository.clearPendingTask()
@@ -281,31 +260,12 @@ class ReaderViewModel @Inject constructor(
     }

     fun onQuickStartEventReceived(event: QuickStartEvent) {
-        if (event.task == getFollowSiteTask()) checkAndStartQuickStartFollowSiteTaskNextStep()
+        if (event.task == getFollowSiteTask()) startQuickStartFollowSiteTask()
     }

-    private fun checkAndStartQuickStartFollowSiteTaskNextStep() {
-        val isDiscover = appPrefsWrapper.getReaderTag()?.isDiscover == true
-        if (isDiscover) {
-            startQuickStartFollowSiteTaskDiscoverTabStep()
-        } else {
-            autoSwitchToDiscoverTab()
-        }
-    }
-
-    private fun autoSwitchToDiscoverTab() {
-        launch {
-            if (!initialized) delay(QUICK_START_DISCOVER_TAB_STEP_DELAY)
-            readerTagsList.find { it.isDiscover }?.let {
-                updateSelectedContent(it)
-            }
-            startQuickStartFollowSiteTaskDiscoverTabStep()
-        }
-    }
-
-    private fun startQuickStartFollowSiteTaskDiscoverTabStep() {
+    private fun startQuickStartFollowSiteTask() {
         val shortMessagePrompt = if (isSettingsSupported()) {
-            R.string.quick_start_dialog_follow_sites_message_short_discover_and_settings
+            R.string.quick_start_dialog_follow_sites_message_short_discover_and_subscriptions
         } else {
             R.string.quick_start_dialog_follow_sites_message_short_discover
         }
@@ -314,16 +274,15 @@ class ReaderViewModel @Inject constructor(
             QuickStartReaderPrompt(
                 getFollowSiteTask(),
                 shortMessagePrompt,
-                R.drawable.ic_cog_white_24dp
+                QuickStartUtils.ICON_NOT_SET,
             )
         )
-        updateContentUiState(showQuickStartFocusPoint = isSettingsSupported())
+        completeQuickStartFollowSiteTaskIfNeeded()
     }

-    fun completeQuickStartFollowSiteTaskIfNeeded() {
+    private fun completeQuickStartFollowSiteTaskIfNeeded() {
         if (quickStartRepository.isPendingTask(getFollowSiteTask())) {
             selectedSiteRepository.getSelectedSite()?.let {
-                hideQuickStartFocusPointIfNeeded()
                 quickStartRepository.completeTask(getFollowSiteTask())
             }
         }
@@ -334,30 +293,9 @@ class ReaderViewModel @Inject constructor(
         isQuickStartPromptShown = false
     }

-    private fun hideQuickStartFocusPointIfNeeded() {
-        val currentUiState = _uiState.value as? ContentUiState
-        if (currentUiState?.settingsMenuItemUiState?.showQuickStartFocusPoint == true) {
-            updateContentUiState(showQuickStartFocusPoint = false)
-        }
-    }
-
     private fun getFollowSiteTask() =
         quickStartRepository.quickStartType.getTaskFromString(QuickStartStore.QUICK_START_FOLLOW_SITE_LABEL)

-    private fun updateContentUiState(
-        showQuickStartFocusPoint: Boolean
-    ) {
-        val currentUiState = _uiState.value as? ContentUiState
-        currentUiState?.let {
-            _uiState.value = currentUiState.copy(
-                settingsMenuItemUiState = it.settingsMenuItemUiState.copy(
-                    isVisible = isSettingsSupported(),
-                    showQuickStartFocusPoint = showQuickStartFocusPoint
-                ),
-            )
-        }
-    }
-
     private fun selectedReaderTag(): ReaderTag? =
         _topBarUiState.value?.let {
             readerTagsList[readerTopBarMenuHelper.getReaderTagIndexFromMenuItem(it.selectedItem)]
@@ -561,30 +499,19 @@ class ReaderViewModel @Inject constructor(
     }

     sealed class ReaderUiState(
-        open val searchMenuItemUiState: MenuItemUiState,
-        open val settingsMenuItemUiState: MenuItemUiState,
         val appBarExpanded: Boolean = false,
         val tabLayoutVisible: Boolean = false
     ) {
         data class ContentUiState(
             val tabUiStates: List<TabUiState>,
             val selectedReaderTag: ReaderTag?,
-            override val searchMenuItemUiState: MenuItemUiState,
-            override val settingsMenuItemUiState: MenuItemUiState,
         ) : ReaderUiState(
-            searchMenuItemUiState = searchMenuItemUiState,
-            settingsMenuItemUiState = settingsMenuItemUiState,
             appBarExpanded = true,
             tabLayoutVisible = true
         ) {
             data class TabUiState(
                 val label: UiString
             )
-
-            data class MenuItemUiState(
-                val isVisible: Boolean,
-                val showQuickStartFocusPoint: Boolean = false
-            )
         }
     }

@@ -596,7 +523,6 @@ class ReaderViewModel @Inject constructor(
     )

     companion object {
-        private const val QUICK_START_DISCOVER_TAB_STEP_DELAY = 2000L
         private const val QUICK_START_PROMPT_DURATION = 5000
         private const val FILTER_UPDATE_DELAY = 50L

diff --git a/WordPress/src/main/res/layout/reader_home_settings_menu_action_layout.xml b/WordPress/src/main/res/layout/reader_home_settings_menu_action_layout.xml
deleted file mode 100644
index 6f0bed3826f6..000000000000
--- a/WordPress/src/main/res/layout/reader_home_settings_menu_action_layout.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:background="?android:attr/actionBarItemBackground">
-
-    <androidx.appcompat.widget.AppCompatImageView
-        android:id="@+id/settings_menu"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginEnd="@dimen/margin_medium_large"
-        android:layout_marginStart="@dimen/margin_medium_large"
-        android:src="@drawable/ic_reader_following_white_24dp"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent"
-        app:tint="?attr/colorOnSurface"
-        tools:text="@string/my_site_menu_tab_title" />
-
-    <org.wordpress.android.widgets.QuickStartFocusPoint
-        android:id="@+id/menu_quick_start_focus_point"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:contentDescription="@string/quick_start_focus_point_description"
-        android:elevation="@dimen/quick_start_focus_point_elevation"
-        android:gravity="end"
-        android:textAlignment="viewEnd"
-        android:visibility="gone"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:size="small" />
-
-</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/WordPress/src/main/res/menu/reader_home.xml b/WordPress/src/main/res/menu/reader_home.xml
deleted file mode 100644
index 3e225ff3f08d..000000000000
--- a/WordPress/src/main/res/menu/reader_home.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto">
-    <item
-        android:id="@+id/menu_settings"
-        app:actionLayout="@layout/reader_home_settings_menu_action_layout"
-        android:title="@string/reader_menu_tags_and_blogs"
-        android:visible="false"
-        app:showAsAction="always" />
-
-    <item
-        android:id="@+id/menu_search"
-        android:icon="@drawable/ic_search_white_24dp"
-        android:title="@string/search"
-        android:visible="false"
-        app:showAsAction="always" />
-</menu>
diff --git a/WordPress/src/main/res/values/strings.xml b/WordPress/src/main/res/values/strings.xml
index 1c2a647c5c95..6e75f529ae1f 100644
--- a/WordPress/src/main/res/values/strings.xml
+++ b/WordPress/src/main/res/values/strings.xml
@@ -3287,7 +3287,7 @@
     <string name="quick_start_dialog_enable_sharing_title" tools:ignore="UnusedResources">Enable post sharing</string>
     <string name="quick_start_dialog_follow_sites_message" translatable="false">@string/quick_start_list_follow_site_subtitle</string>
     <string name="quick_start_dialog_follow_sites_message_short_reader" tools:ignore="UnusedResources">Select %1$s Reader %2$s to find other sites.</string>
-    <string name="quick_start_dialog_follow_sites_message_short_discover_and_settings">Use  Discover  to find sites and tags. Try selecting %1$s Settings %2$s to add topics you like.</string>
+    <string name="quick_start_dialog_follow_sites_message_short_discover_and_subscriptions">Use Discover to find sites and tags. Try selecting Subscriptions to view subscribed content and manage your subscriptions.</string>
     <string name="quick_start_dialog_follow_sites_message_short_discover">Use  Discover  to find sites and tags.</string>
     <string name="quick_start_dialog_upload_media_message_short_plus" tools:ignore="UnusedResources">Select %1$s plus %2$s to upload media. You can add it to your posts/ pages from any device.</string>
     <string name="quick_start_dialog_follow_sites_title">Connect with other sites</string>

diff --git a/WordPress/src/test/java/org/wordpress/android/ui/reader/discover/interests/ReaderInterestsViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/reader/discover/interests/ReaderInterestsViewModelTest.kt
index 1943a6a2f15a..d098e17f6477 100644
--- a/WordPress/src/test/java/org/wordpress/android/ui/reader/discover/interests/ReaderInterestsViewModelTest.kt
+++ b/WordPress/src/test/java/org/wordpress/android/ui/reader/discover/interests/ReaderInterestsViewModelTest.kt
@@ -320,20 +320,6 @@ class ReaderInterestsViewModelTest : BaseUnitTest() {
                 .isEqualTo(false)
         }

-    @Test
-    fun `when interest tag is toggled, then complete follow site quick start task if needed is invoked`() =
-        testWithEmptyUserTags {
-            val interests = getInterests()
-            whenever(readerTagRepository.getInterests()).thenReturn(SuccessWithData(interests))
-            val selectInterestAtIndex = 2
-
-            initViewModel()
-            viewModel.onInterestAtIndexToggled(index = selectInterestAtIndex, isChecked = true)
-
-            // Then
-            verify(parentViewModel).completeQuickStartFollowSiteTaskIfNeeded()
-        }
-
     @Test
     fun `done button shown in enabled state if an interest is in selected state`() =
         testWithEmptyUserTags {
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 3cb4f933e28e..0b57e79121fc 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
@@ -38,6 +38,7 @@ 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
 import org.wordpress.android.ui.reader.viewmodels.ReaderViewModel.ReaderUiState.ContentUiState
+import org.wordpress.android.ui.reader.viewmodels.ReaderViewModel.TopBarUiState
 import org.wordpress.android.util.JetpackBrandingUtils
 import org.wordpress.android.util.SnackbarSequencer
 import org.wordpress.android.viewmodel.Event
@@ -119,7 +120,7 @@ class ReaderViewModelTest : BaseUnitTest() {
         // Arrange
         whenever(appPrefsWrapper.readerTagsUpdatedTimestamp).thenReturn(-1)
         // Act
-        triggerReaderTabContentDisplay()
+        triggerContentDisplay()
         // Assert
         assertThat(viewModel.updateTags.value?.getContentIfNotHandled()).isNotNull
     }
@@ -129,7 +130,7 @@ class ReaderViewModelTest : BaseUnitTest() {
         // Arrange
         whenever(appPrefsWrapper.readerTagsUpdatedTimestamp).thenReturn(DUMMY_CURRENT_TIME - UPDATE_TAGS_THRESHOLD + 1)
         // Act
-        triggerReaderTabContentDisplay()
+        triggerContentDisplay()
         // Assert
         assertThat(viewModel.updateTags.value?.getContentIfNotHandled()).isNull()
     }
@@ -139,7 +140,7 @@ class ReaderViewModelTest : BaseUnitTest() {
         // Arrange
         whenever(appPrefsWrapper.readerTagsUpdatedTimestamp).thenReturn(DUMMY_CURRENT_TIME - UPDATE_TAGS_THRESHOLD - 1)
         // Act
-        triggerReaderTabContentDisplay()
+        triggerContentDisplay()
         // Assert
         assertThat(viewModel.updateTags.value?.getContentIfNotHandled()).isNotNull
     }
@@ -153,7 +154,7 @@ class ReaderViewModelTest : BaseUnitTest() {
         }
         whenever(loadReaderTabsUseCase.loadTabs()).thenReturn(ReaderTagList())
         // Act
-        triggerReaderTabContentDisplay()
+        triggerContentDisplay()
         // Assert
         assertThat(state).isNull()
     }
@@ -166,7 +167,7 @@ class ReaderViewModelTest : BaseUnitTest() {
             state = it
         }
         // Act
-        triggerReaderTabContentDisplay()
+        triggerContentDisplay()
         // Assert
         assertThat(state).isInstanceOf(ContentUiState::class.java)
     }
@@ -282,74 +283,30 @@ class ReaderViewModelTest : BaseUnitTest() {
     @Test
     fun `Search is disabled for self-hosted login`() = testWithNonEmptyTags {
         // Arrange
-        var state: ReaderUiState? = null
-        viewModel.uiState.observeForever {
+        var state: TopBarUiState? = null
+        viewModel.topBarUiState.observeForever {
             state = it
         }
         // Act
-        triggerReaderTabContentDisplay(hasAccessToken = false)
+        triggerContentDisplay(hasAccessToken = false)

         // Assert
-        assertThat(state!!.searchMenuItemUiState.isVisible).isFalse
+        assertThat(state!!.isSearchActionVisible).isFalse
     }

     @Test
     fun `Search is enabled for dot com login`() = testWithNonEmptyTags {
         // Arrange
         whenever(accountStore.hasAccessToken()).thenReturn(true)
-        var state: ReaderUiState? = null
-        viewModel.uiState.observeForever {
-            state = it
-        }
-        // Act
-        triggerReaderTabContentDisplay()
-
-        // Assert
-        assertThat(state!!.searchMenuItemUiState.isVisible).isTrue
-    }
-
-    @Test
-    fun `OnSettingsActionClicked emits showSettings event`() {
-        // Arrange
-        whenever(accountStore.hasAccessToken()).thenReturn(true)
-        var event: Event<Unit>? = null
-        viewModel.showSettings.observeForever {
-            event = it
-        }
-        // Act
-        viewModel.onSettingsActionClicked()
-
-        // Assert
-        assertThat(event).isNotNull
-    }
-
-    @Test
-    fun `Settings menu is disabled for self-hosted login`() = testWithNonEmptyTags {
-        // Arrange
-        var state: ReaderUiState? = null
-        viewModel.uiState.observeForever {
-            state = it
-        }
-        // Act
-        triggerReaderTabContentDisplay(hasAccessToken = false)
-
-        // Assert
-        assertThat(state!!.settingsMenuItemUiState.isVisible).isFalse
-    }
-
-    @Test
-    fun `Settings menu is enabled for dot com login`() = testWithNonEmptyTags {
-        // Arrange
-        whenever(accountStore.hasAccessToken()).thenReturn(true)
-        var state: ReaderUiState? = null
-        viewModel.uiState.observeForever {
+        var state: TopBarUiState? = null
+        viewModel.topBarUiState.observeForever {
             state = it
         }
         // Act
-        triggerReaderTabContentDisplay()
+        triggerContentDisplay()

         // Assert
-        assertThat(state!!.settingsMenuItemUiState.isVisible).isTrue
+        assertThat(state!!.isSearchActionVisible).isTrue
     }

     @Test
@@ -360,7 +317,7 @@ class ReaderViewModelTest : BaseUnitTest() {
             uiStates.add(it)
         }
         // Act
-        triggerReaderTabContentDisplay()
+        triggerContentDisplay()
         // Assert
         assertThat(uiStates.size).isEqualTo(1)
         assertThat(uiStates[0]).isInstanceOf(ContentUiState::class.java)
@@ -375,7 +332,7 @@ class ReaderViewModelTest : BaseUnitTest() {
             uiStates.add(it)
         }
         // Act
-        triggerReaderTabContentDisplay()
+        triggerContentDisplay()
         // Assert
         assertThat(uiStates.size).isEqualTo(1)
         assertThat(uiStates[0]).isInstanceOf(ContentUiState::class.java)
@@ -440,59 +397,49 @@ class ReaderViewModelTest : BaseUnitTest() {
     }

     @Test
-    fun `given discover selected with settings available, when qs event follow site, then discover tab step started`() {
+    fun `given reader selected, when qs event follow site, then qs task started and completed`() {
         val tagList = createNonMockedNonEmptyReaderTagList()
         testWithNonMockedNonEmptyTags(tagList) {
+            whenever(selectedSiteRepository.getSelectedSite()).thenReturn(mock())
+            whenever(quickStartRepository.isPendingTask(QuickStartNewSiteTask.FOLLOW_SITE)).thenReturn(true)
+
             val observers = initObservers()
-            triggerReaderTabContentDisplay(selectedTabReaderTag = tagList[1], hasAccessToken = true)
+            triggerContentDisplay(hasAccessToken = true)

             viewModel.onQuickStartEventReceived(QuickStartEvent(QuickStartNewSiteTask.FOLLOW_SITE))

-            assertQsFollowSiteDiscoverTabStepStarted(observers, isSettingsSupported = true)
+            assertQsFollowSiteTaskStarted(observers, isSettingsSupported = true)
+            assertQsFollowSiteTaskCompleted()
         }
     }

     @Test
-    fun `given discover selected no settings available, when qs event follow site, then discover tab step started`() {
+    fun `given reader selected no settings available, when qs event follow site, then qs task started and completed`() {
         val tagList = createNonMockedNonEmptyReaderTagList()
         testWithNonMockedNonEmptyTags(tagList) {
+            whenever(selectedSiteRepository.getSelectedSite()).thenReturn(mock())
+            whenever(quickStartRepository.isPendingTask(QuickStartNewSiteTask.FOLLOW_SITE)).thenReturn(true)
+
             val observers = initObservers()
-            triggerReaderTabContentDisplay(selectedTabReaderTag = tagList[1], hasAccessToken = false)
+            triggerContentDisplay(hasAccessToken = false)

             viewModel.onQuickStartEventReceived(QuickStartEvent(QuickStartNewSiteTask.FOLLOW_SITE))

-            assertQsFollowSiteDiscoverTabStepStarted(observers, isSettingsSupported = false)
+            assertQsFollowSiteTaskStarted(observers, isSettingsSupported = false)
+            assertQsFollowSiteTaskCompleted()
         }
     }

     @Test
-    fun `given discover tab selected, when quick start event not follow site, then qs discover tab step not started`() {
+    fun `given reader selected, when quick start event not follow site, then qs task not started`() {
         val tagList = createNonMockedNonEmptyReaderTagList()
         testWithNonMockedNonEmptyTags(tagList) {
             val observers = initObservers()
-            triggerReaderTabContentDisplay(selectedTabReaderTag = tagList[1])
+            triggerContentDisplay()

             viewModel.onQuickStartEventReceived(QuickStartEvent(QuickStartNewSiteTask.CHECK_STATS))

-            assertQsFollowSiteDiscoverTabStepNotStarted(observers)
-        }
-    }
-
-    /* QUICK START - SETTING MENU CLICK */
-
-    @Test
-    fun `given pending follow site qs task, when settings menu clicked, then qs follow site task is completed`() {
-        val tagList = createNonMockedNonEmptyReaderTagList()
-        testWithNonMockedNonEmptyTags(tagList) {
-            whenever(accountStore.hasAccessToken()).thenReturn(true)
-            whenever(selectedSiteRepository.getSelectedSite()).thenReturn(mock())
-            whenever(quickStartRepository.isPendingTask(QuickStartNewSiteTask.FOLLOW_SITE)).thenReturn(true)
-            val observers = initObservers()
-            triggerReaderTabContentDisplay(selectedTabReaderTag = tagList[1])
-
-            viewModel.onSettingsActionClicked()
-
-            assertQsFollowSiteTaskCompleted(observers)
+            assertQsFollowSiteTaskNotStarted(observers)
         }
     }

@@ -549,41 +496,33 @@ class ReaderViewModelTest : BaseUnitTest() {
         assertThat(showJetpackOverlayEvent.last().peekContent()).isTrue
     }

-    private fun assertQsFollowSiteDiscoverTabStepStarted(
+    private fun assertQsFollowSiteTaskStarted(
         observers: Observers,
         isSettingsSupported: Boolean = true
     ) {
         with(observers) {
             assertThat(quickStartReaderPrompts.last().peekContent().shortMessagePrompt).isEqualTo(
                 if (isSettingsSupported) {
-                    R.string.quick_start_dialog_follow_sites_message_short_discover_and_settings
+                    R.string.quick_start_dialog_follow_sites_message_short_discover_and_subscriptions
                 } else {
                     R.string.quick_start_dialog_follow_sites_message_short_discover
                 }
             )
-            assertThat(uiStates.last().findSettingsMenuQsFocusPoint()).isEqualTo(isSettingsSupported)
         }
     }

-    private fun assertQsFollowSiteDiscoverTabStepNotStarted(
+    private fun assertQsFollowSiteTaskNotStarted(
         observers: Observers
     ) {
         with(observers) {
             assertThat(quickStartReaderPrompts).isEmpty()
-            assertThat(uiStates.last().findSettingsMenuQsFocusPoint()).isEqualTo(false)
         }
     }

-    private fun assertQsFollowSiteTaskCompleted(
-        observers: Observers
-    ) {
+    private fun assertQsFollowSiteTaskCompleted() {
         verify(quickStartRepository).completeTask(QuickStartNewSiteTask.FOLLOW_SITE)
-        assertThat(observers.uiStates.last().findSettingsMenuQsFocusPoint()).isEqualTo(false)
     }

-    private fun ReaderUiState.findSettingsMenuQsFocusPoint() =
-        (this as? ContentUiState)?.settingsMenuItemUiState?.showQuickStartFocusPoint ?: false
-
     private fun initObservers(): Observers {
         val uiStates = mutableListOf<ReaderUiState>()
         viewModel.uiState.observeForever {
@@ -610,11 +549,9 @@ class ReaderViewModelTest : BaseUnitTest() {
         val tabNavigationEvents: List<TabNavigation>
     )

-    private fun triggerReaderTabContentDisplay(
-        selectedTabReaderTag: ReaderTag? = null,
+    private fun triggerContentDisplay(
         hasAccessToken: Boolean = true
     ) {
-        whenever(appPrefsWrapper.getReaderTag()).thenReturn(selectedTabReaderTag)
         whenever(accountStore.hasAccessToken()).thenReturn(hasAccessToken)
         viewModel.start()
     }


[Reader IA] Fix Discover scrolling to top when loading more posts

Fixes #19968

This prevents the stale information of this field from being kept in the `uiState` and being consumed by the UI incorrectly multiple times. An event makes more sense here since we want the action to happen once at a very specific moment, only that event is triggered.

### Demo
_Note: the first flow shown in the video is the correct scroll-to-top behavior that should happen if the user made a swipe-to-refresh but scrolled the list. The second flow (after the swipe-to-refresh load is finished) is the one that was fixed, which was simply scrolling until the point where the feed fetches more posts._

https://github.com/wordpress-mobile/WordPress-Android/assets/5091503/fcd916ba-c0d0-43be-b8b5-455868763756

-----

## To Test:
1. Install and log into Jetpack
2. Go to the Reader
3. Go to the Discover feed
4. Swipe to refresh the Discover feed
5. Start scrolling the feed (preferably slowly)
6. **Verify** the screen doesn't jump to the top when the progress indicator appears on the bottom
7. You can do steps 4-6 several times to see the original issue doesn't happen anymore

-----

## Regression Notes

1. Potential unintended areas of impact

    - N/A

2. What I did to test those areas of impact (or what existing automated tests I relied on)

    - N/A

3. What automated tests I added (or what prevented me from doing so)

    - Add a unit test for the case that was broken before

-----

## 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.

[Reader][Bug] Discover scrolls to top when loading more posts
### Expected behavior
When scrolling down the Discover feed to load older posts, after doing a pull to refresh, the screen should not scroll back to the top.

### Actual behavior
When scrolling down the Discover feed to load older posts, **after doing a pull to refresh**, the screen scrolls back to the first item, even though it's loading items on the end of the list (older posts).

https://github.com/wordpress-mobile/WordPress-Android/assets/5091503/79db6249-0fb2-41b8-ba0f-8d67b6dce89a

### Steps to reproduce the behavior
1. Install and log into Jetpack
2. Go to the Reader
3. Go to the Discover feed
4. Swipe to refresh the Discover feed
5. Start scrolling the feed (preferably slowly so it's easy to see the issue)
6. When a progress spinner appears on the bottom and the feed jumps back to the first item
7. You can do steps 4-6 several times to see the issue happening consistently

### Initial investigation
It looks like this bug has existed for a while (since 2020 or before), as the culprit code is this one:
https://github.com/wordpress-mobile/WordPress-Android/blob/81060a8036577dac90abf29e483f1d723ca53777/WordPress/src/main/java/org/wordpress/android/ui/reader/discover/ReaderDiscoverViewModel.kt#L277-L279

It only happens on the Discover Feed, and only happens on the first request for more posts after using the pull-to-refresh, because the way that the loading indicator is shown simply copies the current state (code above) modifying the `loadMoreProgressVisibility` to `true`.

The issue is that the latest UI state for the swipe-to-refresh contains a `scrollToTop` variable that is still `true` (since the last pull to refresh UI state had that set that way [here](https://github.com/wordpress-mobile/WordPress-Android/blob/81060a8036577dac90abf29e483f1d723ca53777/WordPress/src/main/java/org/wordpress/android/ui/reader/discover/ReaderDiscoverViewModel.kt#L154-L159) with `swipeToRefreshTriggered` being set [here](https://github.com/wordpress-mobile/WordPress-Android/blob/81060a8036577dac90abf29e483f1d723ca53777/WordPress/src/main/java/org/wordpress/android/ui/reader/discover/ReaderDiscoverViewModel.kt#L507-L513)).

Also, the `ViewModel` controls the state of that `scrollToTop` using a class field `swipeToRefreshTriggered`, and resets only that field, but not the actual UI state used for refreshing the screen after the pull-to-refresh content is updated [here](https://github.com/wordpress-mobile/WordPress-Android/blob/81060a8036577dac90abf29e483f1d723ca53777/WordPress/src/main/java/org/wordpress/android/ui/reader/discover/ReaderDiscoverViewModel.kt#L160). So we have those 2 places controlling the same state but not being synchronized properly.

### Fix suggestion
A quick idea for a fix is making sure that whenever the `_uiState` is updated using `.copy` we also set the `scrollToTop` field to the current `swipeToRefreshTriggered` View Model field.

Note this is far from ideal though, since a more proper fix would be to make sure the `_uiState` is the source of truth, also the `scrollToTop` feels more of an `Event` than an actual `State`, so maybe a solution would be making the `scrollToTop` be a separate Event LiveData/Flow that is observed by the UI and is **not** part of the UI State.

##### Tested on OnePlus 5T, Android 10, JPAndroid 24.0-rc-1
##### Tested on Emulator, Android 140, JPAndroid 21.8 (from around May 2023)

diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/discover/ReaderDiscoverFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/reader/discover/ReaderDiscoverFragment.kt
index bea9580b298a..00918013c71e 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/discover/ReaderDiscoverFragment.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/discover/ReaderDiscoverFragment.kt
@@ -131,9 +131,6 @@ class ReaderDiscoverFragment : ViewPagerFragment(R.layout.reader_discover_fragme
             when (it) {
                 is DiscoverUiState.ContentUiState -> {
                     (recyclerView.adapter as ReaderDiscoverAdapter).update(it.cards)
-                    if (it.scrollToTop) {
-                        recyclerView.scrollToPosition(0)
-                    }
                 }
                 is DiscoverUiState.EmptyUiState -> {
                     uiHelpers.setTextOrHide(actionableEmptyView.title, it.titleResId)
@@ -153,9 +150,10 @@ class ReaderDiscoverFragment : ViewPagerFragment(R.layout.reader_discover_fragme
             ptrLayout.isEnabled = it.swipeToRefreshEnabled
             ptrLayout.isRefreshing = it.reloadProgressVisibility
         }
+        viewModel.scrollToTopEvent.observeEvent(viewLifecycleOwner) { recyclerView.scrollToPosition(0) }
         viewModel.navigationEvents.observeEvent(viewLifecycleOwner) { handleNavigation(it) }
-        viewModel.snackbarEvents.observeEvent(viewLifecycleOwner, { it.showSnackbar() })
-        viewModel.preloadPostEvents.observeEvent(viewLifecycleOwner, { it.addWebViewCachingFragment() })
+        viewModel.snackbarEvents.observeEvent(viewLifecycleOwner) { it.showSnackbar() }
+        viewModel.preloadPostEvents.observeEvent(viewLifecycleOwner) { it.addWebViewCachingFragment() }
         viewModel.start(parentViewModel)
     }

diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/discover/ReaderDiscoverViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/reader/discover/ReaderDiscoverViewModel.kt
index d9b5091f88a2..c9b02681ea84 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/discover/ReaderDiscoverViewModel.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/discover/ReaderDiscoverViewModel.kt
@@ -2,6 +2,7 @@ package org.wordpress.android.ui.reader.discover

 import androidx.lifecycle.LiveData
 import androidx.lifecycle.MediatorLiveData
+import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.Observer
 import androidx.lifecycle.viewModelScope
 import kotlinx.coroutines.CoroutineDispatcher
@@ -79,6 +80,9 @@ class ReaderDiscoverViewModel @Inject constructor(
     private val _preloadPostEvents = MediatorLiveData<Event<PreLoadPostContent>>()
     val preloadPostEvents: LiveData<Event<PreLoadPostContent>> = _preloadPostEvents

+    private val _scrollToTopEvent = MutableLiveData<Event<Unit>>()
+    val scrollToTopEvent: LiveData<Event<Unit>> = _scrollToTopEvent
+
     /**
      * Post which is about to be reblogged after the user selects a target site.
      */
@@ -155,9 +159,11 @@ class ReaderDiscoverViewModel @Inject constructor(
                             convertCardsToUiStates(posts),
                             reloadProgressVisibility = false,
                             loadMoreProgressVisibility = false,
-                            scrollToTop = swipeToRefreshTriggered
                         )
-                        swipeToRefreshTriggered = false
+                        if (swipeToRefreshTriggered) {
+                            _scrollToTopEvent.postValue(Event(Unit))
+                            swipeToRefreshTriggered = false
+                        }
                     } else {
                         _uiState.value = DiscoverUiState.EmptyUiState.ShowNoPostsUiState {
                             _navigationEvents.value = Event(ShowReaderSubs)
@@ -523,7 +529,6 @@ class ReaderDiscoverViewModel @Inject constructor(
         val fullscreenProgressVisibility: Boolean = false,
         val swipeToRefreshEnabled: Boolean = false,
         open val fullscreenEmptyVisibility: Boolean = false,
-        open val scrollToTop: Boolean = false
     ) {
         open val reloadProgressVisibility: Boolean = false
         open val loadMoreProgressVisibility: Boolean = false
@@ -532,7 +537,6 @@ class ReaderDiscoverViewModel @Inject constructor(
             val cards: List<ReaderCardUiState>,
             override val reloadProgressVisibility: Boolean,
             override val loadMoreProgressVisibility: Boolean,
-            override val scrollToTop: Boolean
         ) : DiscoverUiState(contentVisiblity = true, swipeToRefreshEnabled = true)

         object LoadingUiState : DiscoverUiState(fullscreenProgressVisibility = true)

diff --git a/WordPress/src/test/java/org/wordpress/android/ui/reader/discover/ReaderDiscoverViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/reader/discover/ReaderDiscoverViewModelTest.kt
index d3d7227e6465..1db7219634ec 100644
--- a/WordPress/src/test/java/org/wordpress/android/ui/reader/discover/ReaderDiscoverViewModelTest.kt
+++ b/WordPress/src/test/java/org/wordpress/android/ui/reader/discover/ReaderDiscoverViewModelTest.kt
@@ -608,18 +608,19 @@ class ReaderDiscoverViewModelTest : BaseUnitTest() {
     @Test
     fun `Scroll to top is triggered when discover feed is updated after swipe to refresh`() = test {
         // Arrange
-        val uiStates = init().uiStates
+        val scrollToTopCounter = init().scrollToTopCounter
+
         // Act
         viewModel.swipeToRefresh()
         fakeDiscoverFeed.value = createDummyReaderCardsList()
         // Assert
-        assertThat(uiStates.last().scrollToTop).isTrue
+        assertThat(scrollToTopCounter.count).isEqualTo(1)
     }

     @Test
     fun `Scroll to top is not triggered when discover feed is updated after load more action`() = test {
         // Arrange
-        val uiStates = init().uiStates
+        val scrollToTopCounter = init().scrollToTopCounter
         val closeToEndIndex = NUMBER_OF_ITEMS.toInt() - INITIATE_LOAD_MORE_OFFSET
         init()
         // Act
@@ -628,7 +629,34 @@ class ReaderDiscoverViewModelTest : BaseUnitTest() {
         }
         fakeDiscoverFeed.value = createDummyReaderCardsList()
         // Assert
-        assertThat(uiStates.last().scrollToTop).isFalse
+        assertThat(scrollToTopCounter.count).isZero()
+    }
+
+    @Test
+    fun `Scroll to top is triggered only once when discover feed is updated with load more after swipe to refresh`() =
+        test {
+            // Arrange
+            val scrollToTopCounter = init().scrollToTopCounter
+            val closeToEndIndex = NUMBER_OF_ITEMS.toInt() - INITIATE_LOAD_MORE_OFFSET
+
+            // Act for swipe to refresh
+            viewModel.swipeToRefresh()
+            fakeDiscoverFeed.value = createDummyReaderCardsList()
+
+            // Assert for swipe to refresh
+            assertThat(scrollToTopCounter.count).isEqualTo(1)
+
+            // Arrange for load more
+            scrollToTopCounter.reset()
+
+            // Act for load more
+            ((viewModel.uiState.value as ContentUiState).cards[closeToEndIndex] as ReaderPostUiState).let {
+                it.onItemRendered.invoke(it)
+            }
+            fakeDiscoverFeed.value = createDummyReaderCardsList()
+
+            // Assert for load more
+            assertThat(scrollToTopCounter.count).isZero()
     }

     @Test
@@ -679,11 +707,15 @@ class ReaderDiscoverViewModelTest : BaseUnitTest() {
         viewModel.snackbarEvents.observeForever {
             msgs.add(it)
         }
+        val scrollToTop = SimpleCounter()
+        viewModel.scrollToTopEvent.observeForever {
+            scrollToTop.increment()
+        }
         viewModel.start(parentViewModel)
         if (autoUpdateFeed) {
             fakeDiscoverFeed.value = createDummyReaderCardsList()
         }
-        return Observers(uiStates, navigation, msgs)
+        return Observers(uiStates, navigation, msgs, scrollToTop)
     }

     // since we are adding an InterestsYouMayLikeCard we remove one item from the numberOfItems since it counts as 1.
@@ -881,6 +913,18 @@ class ReaderDiscoverViewModelTest : BaseUnitTest() {
     private data class Observers(
         val uiStates: List<DiscoverUiState>,
         val navigation: List<Event<ReaderNavigationEvents>>,
-        val snackbarMsgs: List<Event<SnackbarMessageHolder>>
+        val snackbarMsgs: List<Event<SnackbarMessageHolder>>,
+        val scrollToTopCounter: SimpleCounter, // number of calls to this event
     )
+
+    private class SimpleCounter {
+        var count: Int = 0
+            private set
+
+        fun increment() = count++
+
+        fun reset() {
+            count = 0
+        }
+    }
 }