Fixes: In eligible cards shown in Personalization Screen

## Fixes
#19467

## Description
This PR fixes the scenario in which the in eligible feature cards were shown in the personalization screen This PR adds a check before adding the visibility options for a feature card in personalization screen.

Note: the shortcuts personalization already was checking whether a feature is available for a site before showing it on the screen

| Before  |  After |
|---|---|
|  |  |

## To test:
### Blaze feature is shown/hidden
1. Login to the jetpack app with a site which doesn't have blaze capability -
2. Go to Dashboard
3. Click on Personalize your Home Card
4. Go to Dashboard Cards
5. Verify that blaze card visibility toggle is not shown
6. Login to the Jetpack app with site having blaze capability
7. Follow steps 2-4
8. Verify that blaze card visibility toggle is shown

### Activity log card toggle is shown/hidden
1. Login to the jetpack app with a site which doesn't have Activity log capability
2. Go to Dashboard
3. Click on Personalize your Home Card
4. Go to Dashboard Cards
5. Verify that Activity log visibility toggle is not shown
6. Login to the Jetpack app with site having activity log card capability - (manage content/admin)
7. Follow steps 2-4
8. Verify that Activity log visibility toggle is shown

### Pages Card toggle is shown/hidden

### Pages card role logic

| Role  | Can see pages card |
|---|---|
| Subscriber | N  |
| Contributor   | N  |
| Author | N  |
| Editor  | Y  |
| Administrator | Y  |
| Super Admin  |  Y |

1. Login to the jetpack app with a site which doesn't have Pages capability
2. Go to Dashboard
3. Click on Personalize your Home Card
4. Go to Dashboard Cards
5. Verify that Pages card visibility toggle is not shown
6. Login to the Jetpack app with site having Pages capability - see the table above
7. Follow steps 2-4
8. Verify that Pages card visibility toggle is shown

## Regression Notes
1. Potential unintended areas of impact

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

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

PR submission checklist:
- [x] I have completed the Regression Notes.
- [x] I have considered adding accessibility improvements for my changes.
- [x] I have considered if this change warrants user-facing release notes and have added them to `RELEASE-NOTES.txt` if necessary.

UI Changes testing checklist:
- [x] Portrait and landscape orientations.
- [x] Light and dark modes.
- [x] Fonts: Larger, smaller and bold text.
- [x] High contrast.
- [x] Talkback.
- [x] Languages with large words or with letters/accents not frequently used in English.
- [x] Right-to-left languages. (Even if translation isn’t complete, formatting should still respect the right-to-left layout)
- [x] Large and small screen sizes. (Tablet and smaller phones)
- [x] Multi-tasking: Split screen and Pop-up view. (Android 10 or higher)

Dashboard Cards Personalization shows in eligible cards
### Expected behavior
For a site or user not having the capability of a feature, the card personalization for that card should not be shown

### Actual behavior
The ability to hide/show a card of a feature that isn't available for the user is shown in the dashboard personalization screen

For example, the below screen shows blaze card hide/show ability but the feature is not available on the site

<img src="https://github.com/wordpress-mobile/WordPress-Android/assets/17463767/1e840fe8-77cc-48ee-9318-3d8cfc5ddba3" width="250" height="530">

### Steps to reproduce the behavior
- Go to Jetpack app and login to an account without having Blaze, Activity log or Post edit capability
- Go to Dashboard
- Click on Personalize your Home tab
- Notice that Blaze or Activity Log card hide/show toggle is shown

##### Tested on [device], Android [version], JPAndroid / WPAndroid [version]
Pixel 4a, Android 13

diff --git a/WordPress/src/main/java/org/wordpress/android/ui/mysite/personalization/DashboardCardPersonalizationViewModelSlice.kt b/WordPress/src/main/java/org/wordpress/android/ui/mysite/personalization/DashboardCardPersonalizationViewModelSlice.kt
index 62149c0368bd..8d25b5d32a73 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/mysite/personalization/DashboardCardPersonalizationViewModelSlice.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/mysite/personalization/DashboardCardPersonalizationViewModelSlice.kt
@@ -9,8 +9,11 @@ import kotlinx.coroutines.flow.firstOrNull
 import kotlinx.coroutines.launch
 import org.wordpress.android.R
 import org.wordpress.android.analytics.AnalyticsTracker
+import org.wordpress.android.fluxc.model.SiteModel
 import org.wordpress.android.fluxc.store.BloggingRemindersStore
 import org.wordpress.android.modules.BG_THREAD
+import org.wordpress.android.ui.blaze.BlazeFeatureUtils
+import org.wordpress.android.ui.bloggingprompts.BloggingPromptsSettingsHelper
 import org.wordpress.android.ui.mysite.SelectedSiteRepository
 import org.wordpress.android.ui.mysite.cards.dashboard.posts.PostCardType
 import org.wordpress.android.ui.prefs.AppPrefsWrapper
@@ -24,6 +27,8 @@ class DashboardCardPersonalizationViewModelSlice @Inject constructor(
     private val selectedSiteRepository: SelectedSiteRepository,
     private val bloggingRemindersStore: BloggingRemindersStore,
     private val analyticsTrackerWrapper: AnalyticsTrackerWrapper,
+    private val blazeFeatureUtils: BlazeFeatureUtils,
+    private val bloggingPromptsSettingsHelper: BloggingPromptsSettingsHelper
 ) {
     private val _uiState = MutableLiveData<List<DashboardCardState>>()
     val uiState: LiveData<List<DashboardCardState>> = _uiState
@@ -41,52 +46,90 @@ class DashboardCardPersonalizationViewModelSlice @Inject constructor(
     }

     private suspend fun getCardStates(siteId: Long): List<DashboardCardState> {
-        return listOf(
-            DashboardCardState(
-                title = R.string.personalization_screen_stats_card_title,
-                description = R.string.personalization_screen_stats_card_description,
-                enabled = isStatsCardShown(siteId),
-                cardType = CardType.STATS
-            ),
-            DashboardCardState(
-                title = R.string.personalization_screen_draft_posts_card_title,
-                description = R.string.personalization_screen_draft_posts_card_description,
-                enabled = isDraftPostsCardShown(siteId),
-                cardType = CardType.DRAFT_POSTS
-            ),
-            DashboardCardState(
-                title = R.string.personalization_screen_scheduled_posts_card_title,
-                description = R.string.personalization_screen_scheduled_posts_card_description,
-                enabled = isScheduledPostsCardShown(siteId),
-                cardType = CardType.SCHEDULED_POSTS
-            ),
-            DashboardCardState(
-                title = R.string.personalization_screen_pages_card_title,
-                description = R.string.personalization_screen_pages_card_description,
-                enabled = isPagesCardShown(siteId),
-                cardType = CardType.PAGES
-            ),
-            DashboardCardState(
-                title = R.string.personalization_screen_activity_log_card_title,
-                description = R.string.personalization_screen_activity_log_card_description,
-                enabled = isActivityLogCardShown(siteId),
-                cardType = CardType.ACTIVITY_LOG
-            ),
+        val selectedSite = selectedSiteRepository.getSelectedSite()
+        val dashboardCardStates = mutableListOf<DashboardCardState>()
+        dashboardCardStates.add(statsCard(siteId))
+        dashboardCardStates.add(draftPostsCard(siteId))
+        dashboardCardStates.add(getScheduledPostsCard(siteId))
+        selectedSite?.let {
+            getPagesCardState(siteId, selectedSite)?.let { dashboardCardStates.add(it) }
+            getActivityLogCard(siteId, selectedSite)?.let { dashboardCardStates.add(it) }
+            getBlazeCardState(siteId, selectedSite)?.let { dashboardCardStates.add(it) }
+        }
+        getBloggingPromptCardState()?.let { dashboardCardStates.add(it) }
+        return dashboardCardStates.toList()
+    }
+
+    private suspend fun getBloggingPromptCardState(): DashboardCardState? {
+        return if (bloggingPromptsSettingsHelper.shouldShowPromptsFeature()) {
             DashboardCardState(
+                title = R.string.personalization_screen_blogging_prompts_card_title,
+                description = R.string.personalization_screen_blogging_prompts_card_description,
+                enabled = isPromptsSettingEnabled(selectedSiteRepository.getSelectedSiteLocalId()),
+                cardType = CardType.BLOGGING_PROMPTS
+            )
+        } else null
+    }
+
+    private fun getBlazeCardState(siteId: Long, selectedSite: SiteModel): DashboardCardState? {
+        if (blazeFeatureUtils.isSiteBlazeEligible(selectedSite)) {
+            return DashboardCardState(
                 title = R.string.personalization_screen_blaze_card_title,
                 description = R.string.personalization_screen_blaze_card_description,
                 enabled = isBlazeCardShown(siteId),
                 cardType = CardType.BLAZE
-            ),
+            )
+        }
+        return null
+    }
+
+    private fun getActivityLogCard(siteId: Long, selectedSite: SiteModel): DashboardCardState? {
+        if (selectedSite.hasCapabilityManageOptions && !selectedSite.isWpForTeamsSite) {
+            return DashboardCardState(
+                title = R.string.personalization_screen_activity_log_card_title,
+                description = R.string.personalization_screen_activity_log_card_description,
+                enabled = isActivityLogCardShown(siteId),
+                cardType = CardType.ACTIVITY_LOG
+            )
+        }
+        return null
+    }
+
+    private fun getScheduledPostsCard(siteId: Long) = DashboardCardState(
+        title = R.string.personalization_screen_scheduled_posts_card_title,
+        description = R.string.personalization_screen_scheduled_posts_card_description,
+        enabled = isScheduledPostsCardShown(siteId),
+        cardType = CardType.SCHEDULED_POSTS
+    )
+
+    private fun draftPostsCard(siteId: Long) = DashboardCardState(
+        title = R.string.personalization_screen_draft_posts_card_title,
+        description = R.string.personalization_screen_draft_posts_card_description,
+        enabled = isDraftPostsCardShown(siteId),
+        cardType = CardType.DRAFT_POSTS
+    )
+
+    private fun statsCard(siteId: Long) = DashboardCardState(
+        title = R.string.personalization_screen_stats_card_title,
+        description = R.string.personalization_screen_stats_card_description,
+        enabled = isStatsCardShown(siteId),
+        cardType = CardType.STATS
+    )
+
+    private fun getPagesCardState(siteId: Long, selectedSite: SiteModel): DashboardCardState? {
+        return if (selectedSite.hasCapabilityEditPages || selectedSite.isSelfHostedAdmin) {
             DashboardCardState(
-                title = R.string.personalization_screen_blogging_prompts_card_title,
-                description = R.string.personalization_screen_blogging_prompts_card_description,
-                enabled = isPromptsSettingEnabled(selectedSiteRepository.getSelectedSiteLocalId()),
-                cardType = CardType.BLOGGING_PROMPTS
+                title = R.string.personalization_screen_pages_card_title,
+                description = R.string.personalization_screen_pages_card_description,
+                enabled = isPagesCardShown(siteId),
+                cardType = CardType.PAGES
             )
-        )
+        } else {
+            null
+        }
     }

+
     fun onCardToggled(cardType: CardType, enabled: Boolean) {
         val siteId = selectedSiteRepository.getSelectedSite()!!.siteId
         scope.launch(bgDispatcher) {
@@ -154,6 +197,7 @@ class DashboardCardPersonalizationViewModelSlice @Inject constructor(
     private fun isActivityLogCardShown(siteId: Long) = !appPrefsWrapper.getShouldHideActivityDashboardCard(siteId)

     private fun isBlazeCardShown(siteId: Long) = !appPrefsWrapper.hideBlazeCard(siteId)
+
     private suspend fun isPromptsSettingEnabled(
         siteId: Int
     ): Boolean = bloggingRemindersStore

diff --git a/WordPress/src/test/java/org/wordpress/android/ui/mysite/personalization/DashboardCardPersonalizationViewModelSliceTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/mysite/personalization/DashboardCardPersonalizationViewModelSliceTest.kt
index c6074d3d76fa..6e2530ccf16c 100644
--- a/WordPress/src/test/java/org/wordpress/android/ui/mysite/personalization/DashboardCardPersonalizationViewModelSliceTest.kt
+++ b/WordPress/src/test/java/org/wordpress/android/ui/mysite/personalization/DashboardCardPersonalizationViewModelSliceTest.kt
@@ -16,6 +16,8 @@ import org.wordpress.android.analytics.AnalyticsTracker
 import org.wordpress.android.fluxc.model.BloggingRemindersModel
 import org.wordpress.android.fluxc.model.SiteModel
 import org.wordpress.android.fluxc.store.BloggingRemindersStore
+import org.wordpress.android.ui.blaze.BlazeFeatureUtils
+import org.wordpress.android.ui.bloggingprompts.BloggingPromptsSettingsHelper
 import org.wordpress.android.ui.mysite.SelectedSiteRepository
 import org.wordpress.android.ui.mysite.cards.dashboard.posts.PostCardType
 import org.wordpress.android.ui.prefs.AppPrefsWrapper
@@ -36,6 +38,12 @@ class DashboardCardPersonalizationViewModelSliceTest : BaseUnitTest() {
     @Mock
     lateinit var analyticsTrackerWrapper: AnalyticsTrackerWrapper

+    @Mock
+    lateinit var blazeFeatureUtils: BlazeFeatureUtils
+
+    @Mock
+    lateinit var bloggingPromptsSettingsHelper: BloggingPromptsSettingsHelper
+
     private lateinit var viewModelSlice: DashboardCardPersonalizationViewModelSlice

     private val site = SiteModel().apply { siteId = 123L }
@@ -54,12 +62,19 @@ class DashboardCardPersonalizationViewModelSliceTest : BaseUnitTest() {
         whenever(bloggingRemindersStore.bloggingRemindersModel(localSiteId))
             .thenReturn(flowOf(userSetBloggingRemindersModel))

+        whenever(blazeFeatureUtils.isSiteBlazeEligible(site)).thenReturn(true)
+        test {
+            whenever(bloggingPromptsSettingsHelper.shouldShowPromptsFeature()).thenReturn(true)
+        }
+
         viewModelSlice = DashboardCardPersonalizationViewModelSlice(
             bgDispatcher = testDispatcher(),
             appPrefsWrapper = appPrefsWrapper,
             selectedSiteRepository = selectedSiteRepository,
             bloggingRemindersStore = bloggingRemindersStore,
-            analyticsTrackerWrapper = analyticsTrackerWrapper
+            analyticsTrackerWrapper = analyticsTrackerWrapper,
+            blazeFeatureUtils = blazeFeatureUtils,
+            bloggingPromptsSettingsHelper = bloggingPromptsSettingsHelper
         )

         viewModelSlice.uiState.observeForever {
@@ -103,6 +118,8 @@ class DashboardCardPersonalizationViewModelSliceTest : BaseUnitTest() {
     @Test
     fun `given pages card is not hidden, when cards are fetched, then state is checked`() {
         whenever(appPrefsWrapper.getShouldHidePagesDashboardCard(123L)).thenReturn(false)
+        site.hasCapabilityEditPages = true
+        site.setIsSelfHostedAdmin(true)

         viewModelSlice.start(123L)
         val statsCardState = uiStateList.last().find { it.cardType == CardType.PAGES }
@@ -113,6 +130,8 @@ class DashboardCardPersonalizationViewModelSliceTest : BaseUnitTest() {
     @Test
     fun `given activity log card is not hidden, when cards are fetched, then state is checked`() {
         whenever(appPrefsWrapper.getShouldHideActivityDashboardCard(123L)).thenReturn(false)
+        site.hasCapabilityManageOptions = true
+        site.setIsWpForTeamsSite(false)

         viewModelSlice.start(123L)
         val statsCardState = uiStateList.last().find { it.cardType == CardType.ACTIVITY_LOG }
@@ -124,6 +143,7 @@ class DashboardCardPersonalizationViewModelSliceTest : BaseUnitTest() {
     fun `given blaze card is not hidden, when cards are fetched, then state is checked`() {
         whenever(appPrefsWrapper.hideBlazeCard(123L)).thenReturn(false)

+
         viewModelSlice.start(123L)
         val statsCardState = uiStateList.last().find { it.cardType == CardType.BLAZE }


Attach Purchase Domain screen to Domain Search flow

Fixes #19502

The PR adds the Purchase Domain screen to the existing flow.

Note: I also converted `PurchaseDomainViewModel` to assisted injection-friendly implementation for passing domain id to the viewmodel's parameters, which makes the class easily testable. I wasn't sure if we needed the entire `ProposedDomain` entity for passing a selected domain through the further steps, but it should be easy to replace an ID with an entire `ProposedDomain` entity if needed.

To test:
- Make sure you have enabled domain_management feature flag
- Go to "Me" tab -> Domains -> Plus (+) button
- On the "Search for a domain" screen type any query to the text input
- Tap on any domain suggestion
- [x] Make sure you are navigated to the "Purchase Domain" screen

https://github.com/wordpress-mobile/WordPress-Android/assets/16563318/ed0a8b2a-d25b-4dd7-892c-f89323652f29

## Regression Notes
1. Potential unintended areas of impact
Purchase Domain screen since modified the ViewModel implementation

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)
N/A

PR submission checklist:

- [x] I have completed the Regression Notes.
- [x] I have considered adding accessibility improvements for my changes.
- [x] I have considered if this change warrants user-facing release notes and have added them to `RELEASE-NOTES.txt` if necessary.

UI Changes testing checklist:

- [ ] Portrait and landscape orientations.
- [ ] Light and dark modes.
- [ ] Fonts: Larger, smaller and bold text.
- [ ] High contrast.
- [ ] Talkback.
- [ ] Languages with large words or with letters/accents not frequently used in English.
- [ ] Right-to-left languages. (Even if translation isn’t complete, formatting should still respect the right-to-left layout)
- [ ] Large and small screen sizes. (Tablet and smaller phones)
- [ ] Multi-tasking: Split screen and Pop-up view. (Android 10 or higher)

🤖 Link up the purchase domain screen to the flow
Link up the implemented "Purchase Domain" screen UI https://github.com/wordpress-mobile/WordPress-Android/pull/19332 to the flow

diff --git a/WordPress/src/main/java/org/wordpress/android/ui/ActivityNavigator.kt b/WordPress/src/main/java/org/wordpress/android/ui/ActivityNavigator.kt
index 9ae93dd4e3b6..c6e3cc7aea9e 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/ActivityNavigator.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/ActivityNavigator.kt
@@ -11,6 +11,10 @@ import org.wordpress.android.ui.blaze.blazecampaigns.campaignlisting.CampaignLis
 import org.wordpress.android.ui.blaze.blazepromote.ARG_BLAZE_FLOW_SOURCE
 import org.wordpress.android.ui.blaze.blazepromote.ARG_BLAZE_SHOULD_SHOW_OVERLAY
 import org.wordpress.android.ui.blaze.blazepromote.BlazePromoteParentActivity
+import org.wordpress.android.ui.domains.management.purchasedomain.PurchaseDomainActivity
+import org.wordpress.android.ui.domains.management.purchasedomain.PurchaseDomainActivity.Companion.PICKED_DOMAIN_KEY
+import org.wordpress.android.ui.domains.management.purchasedomain.PurchaseDomainActivity.Companion.PICKED_DOMAIN_PRIVACY
+import org.wordpress.android.ui.domains.management.purchasedomain.PurchaseDomainActivity.Companion.PICKED_PRODUCT_ID
 import org.wordpress.android.ui.mysite.menu.KEY_QUICK_START_EVENT
 import org.wordpress.android.ui.mysite.menu.MenuActivity
 import org.wordpress.android.ui.mysite.personalization.PersonalizationActivity
@@ -79,4 +83,13 @@ class ActivityNavigator @Inject constructor() {
         }
         context.startActivity(Intent(context, MenuActivity::class.java))
     }
+
+    fun openPurchaseDomain(context: Context, productId: Int, domainName: String, domainSupportsPrivacy: Boolean) {
+        context.startActivity(
+            Intent(context, PurchaseDomainActivity::class.java)
+                .putExtra(PICKED_PRODUCT_ID, productId)
+                .putExtra(PICKED_DOMAIN_KEY, domainName)
+                .putExtra(PICKED_DOMAIN_PRIVACY, domainSupportsPrivacy)
+        )
+    }
 }
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/domains/management/newdomainsearch/NewDomainSearchActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/domains/management/newdomainsearch/NewDomainSearchActivity.kt
index fc37ca4006f7..ceab64625fb9 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/domains/management/newdomainsearch/NewDomainSearchActivity.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/domains/management/newdomainsearch/NewDomainSearchActivity.kt
@@ -45,8 +45,9 @@ class NewDomainSearchActivity : AppCompatActivity() {

     private fun handleActionEvents(actionEvent: NewDomainSearchViewModel.ActionEvent) {
         when (actionEvent) {
-            is NewDomainSearchViewModel.ActionEvent.PurchaseDomain -> { /* TODO */
-            }
+            is NewDomainSearchViewModel.ActionEvent.PurchaseDomain -> activityNavigator.openPurchaseDomain(
+                this, actionEvent.productId, actionEvent.domain, actionEvent.supportsPrivacy
+            )

             is NewDomainSearchViewModel.ActionEvent.TransferDomain -> activityNavigator.openDomainTransfer(
                 this, actionEvent.url
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/domains/management/newdomainsearch/NewDomainSearchViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/domains/management/newdomainsearch/NewDomainSearchViewModel.kt
index 2c6711efb4c8..9c79f4b6a217 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/domains/management/newdomainsearch/NewDomainSearchViewModel.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/domains/management/newdomainsearch/NewDomainSearchViewModel.kt
@@ -77,7 +77,13 @@ class NewDomainSearchViewModel @Inject constructor(
             mapOf("domain_name" to domain.domain)
         )
         launch {
-            _actionEvents.emit(ActionEvent.PurchaseDomain(domain))
+            _actionEvents.emit(
+                ActionEvent.PurchaseDomain(
+                    productId = domain.productId,
+                    domain = domain.domain,
+                    supportsPrivacy = domain.supportsPrivacy
+                )
+            )
         }
     }

@@ -88,7 +94,11 @@ class NewDomainSearchViewModel @Inject constructor(
     }

     sealed class ActionEvent {
-        data class PurchaseDomain(val domain: ProposedDomain) : ActionEvent()
+        data class PurchaseDomain(
+            val productId: Int,
+            val domain: String,
+            val supportsPrivacy: Boolean
+        ) : ActionEvent()
         data class TransferDomain(val url: String) : ActionEvent()
         object GoBack : ActionEvent()
     }
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/domains/management/newdomainsearch/composable/NewDomainSearchScreen.kt b/WordPress/src/main/java/org/wordpress/android/ui/domains/management/newdomainsearch/composable/NewDomainSearchScreen.kt
index de67eab60f6e..09c92a4ecabe 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/domains/management/newdomainsearch/composable/NewDomainSearchScreen.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/domains/management/newdomainsearch/composable/NewDomainSearchScreen.kt
@@ -231,12 +231,14 @@ fun NewDomainSearchScreenPreview() {
                     domain = "example.com",
                     price = "USD 100",
                     salePrice = "USD 30",
+                    supportsPrivacy = true,
                 ),
                 ProposedDomain(
                     productId = 0,
                     domain = "example.blog",
                     price = "USD 100",
                     salePrice = null,
+                    supportsPrivacy = true,
                 ),
             )
         ),
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/domains/management/newdomainsearch/domainsfetcher/NewDomainsSearchRepository.kt b/WordPress/src/main/java/org/wordpress/android/ui/domains/management/newdomainsearch/domainsfetcher/NewDomainsSearchRepository.kt
index 5d2400a8fb98..cdfcb1589a03 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/domains/management/newdomainsearch/domainsfetcher/NewDomainsSearchRepository.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/domains/management/newdomainsearch/domainsfetcher/NewDomainsSearchRepository.kt
@@ -50,6 +50,7 @@ class NewDomainsSearchRepository @Inject constructor(
                         domain = domain.domain_name,
                         price = domain.cost,
                         salePrice = product?.combinedSaleCostDisplay,
+                        supportsPrivacy = domain.supports_privacy
                     )
                 }
             DomainsResult.Success(suggestions)
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/domains/management/newdomainsearch/domainsfetcher/ProposedDomain.kt b/WordPress/src/main/java/org/wordpress/android/ui/domains/management/newdomainsearch/domainsfetcher/ProposedDomain.kt
index dfaa01a83e8b..61971ac1ccd8 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/domains/management/newdomainsearch/domainsfetcher/ProposedDomain.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/domains/management/newdomainsearch/domainsfetcher/ProposedDomain.kt
@@ -4,7 +4,8 @@ data class ProposedDomain(
     val productId: Int,
     val domain: String,
     val price: String,
-    val salePrice: String?
+    val salePrice: String?,
+    val supportsPrivacy: Boolean,
 ) {
     val domainSuffix: String
         get() = domain.split('.').last()
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/domains/management/purchasedomain/PurchaseDomainActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/domains/management/purchasedomain/PurchaseDomainActivity.kt
index f1a1856f921e..cd1d0a6502cf 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/domains/management/purchasedomain/PurchaseDomainActivity.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/domains/management/purchasedomain/PurchaseDomainActivity.kt
@@ -11,14 +11,26 @@ import kotlinx.coroutines.flow.onEach
 import org.wordpress.android.ui.domains.management.M3Theme
 import org.wordpress.android.ui.domains.management.purchasedomain.PurchaseDomainViewModel.ActionEvent.GoBack
 import org.wordpress.android.ui.domains.management.purchasedomain.PurchaseDomainViewModel.ActionEvent.GoToDomainPurchasing
-import org.wordpress.android.ui.domains.management.purchasedomain.PurchaseDomainViewModel.ActionEvent.GoToExistingDomain
+import org.wordpress.android.ui.domains.management.purchasedomain.PurchaseDomainViewModel.ActionEvent.GoToExistingSite
 import org.wordpress.android.ui.domains.management.purchasedomain.composable.PurchaseDomainScreen
+import javax.inject.Inject

 private typealias NotImplemented = Unit

 @AndroidEntryPoint
 class PurchaseDomainActivity : AppCompatActivity() {
-    private val viewModel: PurchaseDomainViewModel by viewModels()
+    @Inject
+    lateinit var viewModelFactory: PurchaseDomainViewModel.Factory
+
+    private val viewModel: PurchaseDomainViewModel by viewModels {
+        PurchaseDomainViewModel.provideFactory(viewModelFactory, productArg, domainArg, privacyArg)
+    }
+
+    private val productArg: Int get() = intent.getIntExtra(PICKED_PRODUCT_ID, 0)
+
+    private val domainArg: String get() = intent.getStringExtra(PICKED_DOMAIN_KEY) ?: error("Domain cannot be null.")
+
+    private val privacyArg: Boolean get() = intent.getBooleanExtra(PICKED_DOMAIN_PRIVACY, false)

     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
@@ -26,7 +38,7 @@ class PurchaseDomainActivity : AppCompatActivity() {
             M3Theme {
                 PurchaseDomainScreen(
                     onNewDomainCardSelected = viewModel::onNewDomainSelected,
-                    onExistingDomainCardSelected = viewModel::onExistingDomainSelected,
+                    onExistingSiteCardSelected = viewModel::onExistingSiteSelected,
                     onBackPressed = viewModel::onBackPressed,
                 )
             }
@@ -40,9 +52,15 @@ class PurchaseDomainActivity : AppCompatActivity() {

     private fun handleActionEvents(actionEvent: PurchaseDomainViewModel.ActionEvent) {
         when (actionEvent) {
+            is GoToDomainPurchasing -> NotImplemented
+            is GoToExistingSite -> NotImplemented
             GoBack -> onBackPressedDispatcher.onBackPressed()
-            GoToDomainPurchasing -> NotImplemented
-            GoToExistingDomain -> NotImplemented
         }
     }
+
+    companion object {
+        const val PICKED_PRODUCT_ID: String = "picked_product_id"
+        const val PICKED_DOMAIN_KEY: String = "picked_domain_key"
+        const val PICKED_DOMAIN_PRIVACY: String = "picked_domain_privacy"
+    }
 }
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/domains/management/purchasedomain/PurchaseDomainViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/domains/management/purchasedomain/PurchaseDomainViewModel.kt
index ded1ed2d991e..57446e99e186 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/domains/management/purchasedomain/PurchaseDomainViewModel.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/domains/management/purchasedomain/PurchaseDomainViewModel.kt
@@ -1,6 +1,10 @@
 package org.wordpress.android.ui.domains.management.purchasedomain

-import dagger.hilt.android.lifecycle.HiltViewModel
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
@@ -8,13 +12,14 @@ import org.wordpress.android.analytics.AnalyticsTracker.Stat
 import org.wordpress.android.modules.UI_THREAD
 import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper
 import org.wordpress.android.viewmodel.ScopedViewModel
-import javax.inject.Inject
 import javax.inject.Named

-@HiltViewModel
-class PurchaseDomainViewModel @Inject constructor(
+class PurchaseDomainViewModel @AssistedInject constructor(
     @Named(UI_THREAD) private val mainDispatcher: CoroutineDispatcher,
-    private val analyticsTracker: AnalyticsTrackerWrapper
+    private val analyticsTracker: AnalyticsTrackerWrapper,
+    @Assisted private val productId: Int,
+    @Assisted private val domain: String,
+    @Assisted private val privacy: Boolean,
 ) : ScopedViewModel(mainDispatcher) {
     private val _actionEvents = MutableSharedFlow<ActionEvent>()
     val actionEvents: Flow<ActionEvent> = _actionEvents
@@ -26,14 +31,14 @@ class PurchaseDomainViewModel @Inject constructor(
     fun onNewDomainSelected() {
         analyticsTracker.track(Stat.DOMAIN_MANAGEMENT_PURCHASE_DOMAIN_SCREEN_NEW_DOMAIN_TAPPED)
         launch {
-            _actionEvents.emit(ActionEvent.GoToDomainPurchasing)
+            _actionEvents.emit(ActionEvent.GoToDomainPurchasing(domain = domain))
         }
     }

-    fun onExistingDomainSelected() {
-        analyticsTracker.track(Stat.DOMAIN_MANAGEMENT_PURCHASE_DOMAIN_SCREEN_EXISTING_DOMAIN_TAPPED)
+    fun onExistingSiteSelected() {
+        analyticsTracker.track(Stat.DOMAIN_MANAGEMENT_PURCHASE_DOMAIN_SCREEN_EXISTING_SITE_TAPPED)
         launch {
-            _actionEvents.emit(ActionEvent.GoToExistingDomain)
+            _actionEvents.emit(ActionEvent.GoToExistingSite(domain = domain))
         }
     }

@@ -45,7 +50,21 @@ class PurchaseDomainViewModel @Inject constructor(

     sealed class ActionEvent {
         object GoBack : ActionEvent()
-        object GoToDomainPurchasing : ActionEvent()
-        object GoToExistingDomain : ActionEvent()
+        data class GoToDomainPurchasing(val domain: String) : ActionEvent()
+        data class GoToExistingSite(val domain: String) : ActionEvent()
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(productId: Int, domain: String, privacy: Boolean): PurchaseDomainViewModel
+    }
+
+    companion object {
+        fun provideFactory(assistedFactory: Factory, productId: Int, domain: String, privacy: Boolean) =
+            object : ViewModelProvider.Factory {
+                @Suppress("UNCHECKED_CAST")
+                override fun <T : ViewModel> create(modelClass: Class<T>): T =
+                    assistedFactory.create(productId, domain, privacy) as T
+            }
     }
 }
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/domains/management/purchasedomain/composable/PurchaseDomainScreen.kt b/WordPress/src/main/java/org/wordpress/android/ui/domains/management/purchasedomain/composable/PurchaseDomainScreen.kt
index 9b79768d7f9b..892229fde584 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/domains/management/purchasedomain/composable/PurchaseDomainScreen.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/domains/management/purchasedomain/composable/PurchaseDomainScreen.kt
@@ -44,7 +44,7 @@ import org.wordpress.android.ui.domains.management.success
 @Composable
 fun PurchaseDomainScreen(
     onNewDomainCardSelected: () -> Unit,
-    onExistingDomainCardSelected: () -> Unit,
+    onExistingSiteCardSelected: () -> Unit,
     onBackPressed: () -> Unit,
     modifier: Modifier = Modifier
 ) {
@@ -80,7 +80,7 @@ fun PurchaseDomainScreen(
                 ) {
                     ScreenHeader()
                     ScreenDescription()
-                    DomainCards(onNewDomainCardSelected, onExistingDomainCardSelected)
+                    DomainCards(onNewDomainCardSelected, onExistingSiteCardSelected)
                     DiscountNotice()
                 }
             }
@@ -91,7 +91,7 @@ fun PurchaseDomainScreen(
 @Composable
 private fun DomainCards(
     onNewDomainCardSelected: () -> Unit,
-    onExistingDomainCardSelected: () -> Unit,
+    onExistingSiteCardSelected: () -> Unit,
     modifier: Modifier = Modifier,
 ) {
     Box(modifier = modifier.padding(top = 24.dp)) {
@@ -99,12 +99,12 @@ private fun DomainCards(
         if (isPortrait) {
             Column(verticalArrangement = arrangement) {
                 NewDomainCard(onNewDomainCardSelected)
-                ExistingDomainCard(onExistingDomainCardSelected)
+                ExistingSiteCard(onExistingSiteCardSelected)
             }
         } else {
             Row(horizontalArrangement = arrangement) {
                 NewDomainCard(onNewDomainCardSelected, modifier = Modifier.weight(1f))
-                ExistingDomainCard(onExistingDomainCardSelected, modifier = Modifier.weight(1f))
+                ExistingSiteCard(onExistingSiteCardSelected, modifier = Modifier.weight(1f))
             }
         }
     }
@@ -163,8 +163,8 @@ private fun NewDomainCard(
 }

 @Composable
-private fun ExistingDomainCard(
-    onExistingDomainCardSelected: () -> Unit,
+private fun ExistingSiteCard(
+    onExistingSiteCardSelected: () -> Unit,
     modifier: Modifier = Modifier
 ) {
     DomainOptionCard(
@@ -172,7 +172,7 @@ private fun ExistingDomainCard(
         title = R.string.purchase_domain_screen_existing_domain_card_title,
         description = R.string.purchase_domain_screen_existing_domain_card_description,
         button = R.string.purchase_domain_screen_existing_domain_card_button,
-        onOptionSelected = onExistingDomainCardSelected,
+        onOptionSelected = onExistingSiteCardSelected,
         modifier = modifier,
     )
 }
diff --git a/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTracker.java b/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTracker.java
index 20bfd1f1b708..514c5c1df83d 100644
--- a/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTracker.java
+++ b/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTracker.java
@@ -1079,7 +1079,7 @@ public enum Stat {
         MORE_MENU_ITEM_TAPPED,
         DOMAIN_MANAGEMENT_PURCHASE_DOMAIN_SCREEN_SHOWN,
         DOMAIN_MANAGEMENT_PURCHASE_DOMAIN_SCREEN_NEW_DOMAIN_TAPPED,
-        DOMAIN_MANAGEMENT_PURCHASE_DOMAIN_SCREEN_EXISTING_DOMAIN_TAPPED,
+        DOMAIN_MANAGEMENT_PURCHASE_DOMAIN_SCREEN_EXISTING_SITE_TAPPED,
         DOMAIN_MANAGEMENT_SEARCH_FOR_A_DOMAIN_SCREEN_SHOWN,
         DOMAIN_MANAGEMENT_SEARCH_DOMAIN_TAPPED,
         DOMAIN_MANAGEMENT_TRANSFER_DOMAIN_TAPPED,
diff --git a/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTrackerNosara.java b/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTrackerNosara.java
index a0c4889ccb00..23fee7eb0bc7 100644
--- a/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTrackerNosara.java
+++ b/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTrackerNosara.java
@@ -2643,8 +2643,8 @@ public static String getEventNameForStat(AnalyticsTracker.Stat stat) {
                 return "domain_management_purchase_domain_screen_shown";
             case DOMAIN_MANAGEMENT_PURCHASE_DOMAIN_SCREEN_NEW_DOMAIN_TAPPED:
                 return "domain_management_purchase_domain_screen_new_domain_tapped";
-            case DOMAIN_MANAGEMENT_PURCHASE_DOMAIN_SCREEN_EXISTING_DOMAIN_TAPPED:
-                return "domain_management_purchase_domain_screen_existing_domain_tapped";
+            case DOMAIN_MANAGEMENT_PURCHASE_DOMAIN_SCREEN_EXISTING_SITE_TAPPED:
+                return "domain_management_purchase_domain_screen_existing_site_tapped";
             case DOMAIN_MANAGEMENT_SEARCH_FOR_A_DOMAIN_SCREEN_SHOWN:
                 return "domain_management_search_for_a_domain_screen_shown";
             case DOMAIN_MANAGEMENT_SEARCH_DOMAIN_TAPPED:

diff --git a/WordPress/src/test/java/org/wordpress/android/ui/domains/management/newdomainsearch/NewDomainSearchViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/domains/management/newdomainsearch/NewDomainSearchViewModelTest.kt
index 123966533a6d..6298a2ee56d5 100644
--- a/WordPress/src/test/java/org/wordpress/android/ui/domains/management/newdomainsearch/NewDomainSearchViewModelTest.kt
+++ b/WordPress/src/test/java/org/wordpress/android/ui/domains/management/newdomainsearch/NewDomainSearchViewModelTest.kt
@@ -53,17 +53,17 @@ class NewDomainSearchViewModelTest : BaseUnitTest() {

     @Test
     fun `WHEN a domain is tapped THEN send PurchaseDomain action event`() = testWithActionEvents { events ->
-        val domain = ProposedDomain(1, "test.com", "1", null)
+        val domain = ProposedDomain(1, "test.com", "1", null, true)
         viewModel.onDomainTapped(domain)
         advanceUntilIdle()

-        val expectedEvent = ActionEvent.PurchaseDomain(domain)
+        val expectedEvent = ActionEvent.PurchaseDomain(domain.productId, domain.domain, domain.supportsPrivacy)
         assertThat(events.last()).isEqualTo(expectedEvent)
     }

     @Test
     fun `WHEN a domain is tapped THEN track DOMAIN_MANAGEMENT_SEARCH_DOMAIN_TAPPED event`() = test {
-        val domain = ProposedDomain(1, "test.com", "1", null)
+        val domain = ProposedDomain(1, "test.com", "1", null, true)
         viewModel.onDomainTapped(domain)
         advanceUntilIdle()

@@ -149,7 +149,8 @@ class NewDomainSearchViewModelTest : BaseUnitTest() {
                     productId = 0,
                     domain = "domain.com",
                     price = "USD 100",
-                    salePrice = null
+                    salePrice = null,
+                    supportsPrivacy = true
                 )
             )
             val result = DomainsResult.Success(domains)
diff --git a/WordPress/src/test/java/org/wordpress/android/ui/domains/management/newdomainsearch/domainsfetcher/NewDomainsSearchRepositoryTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/domains/management/newdomainsearch/domainsfetcher/NewDomainsSearchRepositoryTest.kt
index 7375e9080d3e..f2ecdee912b8 100644
--- a/WordPress/src/test/java/org/wordpress/android/ui/domains/management/newdomainsearch/domainsfetcher/NewDomainsSearchRepositoryTest.kt
+++ b/WordPress/src/test/java/org/wordpress/android/ui/domains/management/newdomainsearch/domainsfetcher/NewDomainsSearchRepositoryTest.kt
@@ -47,7 +47,8 @@ class NewDomainsSearchRepositoryTest : BaseUnitTest() {
                             productId = 0,
                             domain = "example.com",
                             price = "USD 50",
-                            salePrice = "USD 10"
+                            salePrice = "USD 10",
+                            supportsPrivacy = true
                         )
                     )
                 )
@@ -69,13 +70,15 @@ class NewDomainsSearchRepositoryTest : BaseUnitTest() {
                             productId = 0,
                             domain = "example.com",
                             price = "USD 50",
-                            salePrice = null
+                            salePrice = null,
+                            supportsPrivacy = true
                         )
                     )
                 )
             )
         }

+    @Suppress("LongMethod")
     @Test
     fun `GIVEN few domains with different relevance WHEN searchForDomains THEN sort domains by descending by relevance`() =
         test {
@@ -90,6 +93,7 @@ class NewDomainsSearchRepositoryTest : BaseUnitTest() {
                             is_free = false
                             relevance = 1f
                             cost = "USD 30"
+                            supports_privacy = true
                         },
                         DomainSuggestionResponse().apply {
                             product_id = 1
@@ -97,6 +101,7 @@ class NewDomainsSearchRepositoryTest : BaseUnitTest() {
                             is_free = false
                             relevance = 2f
                             cost = "USD 40"
+                            supports_privacy = true
                         },
                         DomainSuggestionResponse().apply {
                             product_id = 2
@@ -104,6 +109,7 @@ class NewDomainsSearchRepositoryTest : BaseUnitTest() {
                             is_free = false
                             relevance = 0f
                             cost = "USD 50"
+                            supports_privacy = true
                         },
                     )
                 )
@@ -118,19 +124,21 @@ class NewDomainsSearchRepositoryTest : BaseUnitTest() {
                             productId = 1,
                             domain = "second.com",
                             price = "USD 40",
-                            salePrice = null
+                            salePrice = null,
+                            supportsPrivacy = true
                         ),
                         ProposedDomain(
                             productId = 0,
                             domain = "first.com",
                             price = "USD 30",
-                            salePrice = null
+                            salePrice = null,
+                            supportsPrivacy = true
                         ),
-                        ProposedDomain(
-                            productId = 2,
+                        ProposedDomain(productId = 2,
                             domain = "third.com",
                             price = "USD 50",
-                            salePrice = null
+                            salePrice = null,
+                            supportsPrivacy = true
                         ),
                     )
                 )
@@ -151,6 +159,7 @@ class NewDomainsSearchRepositoryTest : BaseUnitTest() {
                             is_free = false
                             relevance = 1f
                             cost = "USD 30"
+                            supports_privacy = true
                         },
                         DomainSuggestionResponse().apply {
                             product_id = 1
@@ -158,6 +167,7 @@ class NewDomainsSearchRepositoryTest : BaseUnitTest() {
                             is_free = false
                             relevance = 2f
                             cost = "USD 40"
+                            supports_privacy = true
                         },
                         DomainSuggestionResponse().apply {
                             product_id = 2
@@ -165,6 +175,7 @@ class NewDomainsSearchRepositoryTest : BaseUnitTest() {
                             is_free = true
                             relevance = 0f
                             cost = "USD 50"
+                            supports_privacy = true
                         },
                     )
                 )
@@ -179,13 +190,15 @@ class NewDomainsSearchRepositoryTest : BaseUnitTest() {
                             productId = 1,
                             domain = "second.com",
                             price = "USD 40",
-                            salePrice = null
+                            salePrice = null,
+                            supportsPrivacy = true
                         ),
                         ProposedDomain(
                             productId = 0,
                             domain = "first.com",
                             price = "USD 30",
-                            salePrice = null
+                            salePrice = null,
+                            supportsPrivacy = true
                         ),
                     )
                 )
@@ -207,7 +220,8 @@ class NewDomainsSearchRepositoryTest : BaseUnitTest() {
                             productId = 0,
                             domain = "example.com",
                             price = "USD 50",
-                            salePrice = null
+                            salePrice = null,
+                            supportsPrivacy = true
                         )
                     )
                 )
@@ -251,7 +265,7 @@ class NewDomainsSearchRepositoryTest : BaseUnitTest() {
     private suspend fun mockProductWithSaleResponse(productId: Int = 0) {
         whenever(productsStore.fetchProducts(Constants.TYPE_DOMAINS_PRODUCT)).thenReturn(
             ProductsStore.OnProductsFetched(
-                products = listOf(Product(productId = productId, combinedSaleCostDisplay = "USD 10"))
+                products = listOf(Product(productId = productId, combinedSaleCostDisplay = "USD 10",))
             )
         )
     }
@@ -278,6 +292,7 @@ class NewDomainsSearchRepositoryTest : BaseUnitTest() {
                         is_free = isFree
                         relevance = 0f
                         cost = "USD 50"
+                        supports_privacy = true
                     }
                 )
             )
diff --git a/WordPress/src/test/java/org/wordpress/android/ui/domains/management/purchasedomain/PurchaseDomainViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/domains/management/purchasedomain/PurchaseDomainViewModelTest.kt
index b375dbe69e84..2d66066b0e2f 100644
--- a/WordPress/src/test/java/org/wordpress/android/ui/domains/management/purchasedomain/PurchaseDomainViewModelTest.kt
+++ b/WordPress/src/test/java/org/wordpress/android/ui/domains/management/purchasedomain/PurchaseDomainViewModelTest.kt
@@ -11,19 +11,23 @@ import org.mockito.Mock
 import org.mockito.MockitoAnnotations
 import org.mockito.kotlin.verify
 import org.wordpress.android.BaseUnitTest
-import org.wordpress.android.analytics.AnalyticsTracker.Stat.DOMAIN_MANAGEMENT_PURCHASE_DOMAIN_SCREEN_EXISTING_DOMAIN_TAPPED
+import org.wordpress.android.analytics.AnalyticsTracker.Stat.DOMAIN_MANAGEMENT_PURCHASE_DOMAIN_SCREEN_EXISTING_SITE_TAPPED
 import org.wordpress.android.analytics.AnalyticsTracker.Stat.DOMAIN_MANAGEMENT_PURCHASE_DOMAIN_SCREEN_NEW_DOMAIN_TAPPED
 import org.wordpress.android.analytics.AnalyticsTracker.Stat.DOMAIN_MANAGEMENT_PURCHASE_DOMAIN_SCREEN_SHOWN
 import org.wordpress.android.ui.domains.management.purchasedomain.PurchaseDomainViewModel.ActionEvent
 import org.wordpress.android.ui.domains.management.purchasedomain.PurchaseDomainViewModel.ActionEvent.GoBack
 import org.wordpress.android.ui.domains.management.purchasedomain.PurchaseDomainViewModel.ActionEvent.GoToDomainPurchasing
-import org.wordpress.android.ui.domains.management.purchasedomain.PurchaseDomainViewModel.ActionEvent.GoToExistingDomain
+import org.wordpress.android.ui.domains.management.purchasedomain.PurchaseDomainViewModel.ActionEvent.GoToExistingSite
 import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper

 @ExperimentalCoroutinesApi
 class PurchaseDomainViewModelTest : BaseUnitTest() {
     @Mock
     private lateinit var analyticsTracker: AnalyticsTrackerWrapper
+    private val productId = 8
+    private val domain = "domain.com"
+    private val supportsPrivacy = true
+
     private lateinit var viewModel: PurchaseDomainViewModel

     @Before
@@ -31,7 +35,10 @@ class PurchaseDomainViewModelTest : BaseUnitTest() {
         MockitoAnnotations.openMocks(this)
         viewModel = PurchaseDomainViewModel(
             mainDispatcher = testDispatcher(),
-            analyticsTracker = analyticsTracker
+            analyticsTracker = analyticsTracker,
+            productId = productId,
+            domain = domain,
+            privacy = supportsPrivacy,
         )
     }

@@ -49,8 +56,8 @@ class PurchaseDomainViewModelTest : BaseUnitTest() {
     @Test
     @Suppress("MaxLineLength")
     fun `WHEN existing domain selected THEN track DOMAIN_MANAGEMENT_PURCHASE_DOMAIN_SCREEN_EXISTING_DOMAIN_TAPPED event`() {
-        viewModel.onExistingDomainSelected()
-        verify(analyticsTracker).track(DOMAIN_MANAGEMENT_PURCHASE_DOMAIN_SCREEN_EXISTING_DOMAIN_TAPPED)
+        viewModel.onExistingSiteSelected()
+        verify(analyticsTracker).track(DOMAIN_MANAGEMENT_PURCHASE_DOMAIN_SCREEN_EXISTING_SITE_TAPPED)
     }

     @Test
@@ -58,15 +65,15 @@ class PurchaseDomainViewModelTest : BaseUnitTest() {
         viewModel.onNewDomainSelected()
         advanceUntilIdle()

-        assertThat(events.last()).isEqualTo(GoToDomainPurchasing)
+        assertThat(events.last()).isEqualTo(GoToDomainPurchasing(domain))
     }

     @Test
-    fun `WHEN existing domain selected THEN send GoToExistingDomain action event`() = testWithActionEvents { events ->
-        viewModel.onExistingDomainSelected()
+    fun `WHEN existing site selected THEN send GoToExistingSite action event`() = testWithActionEvents { events ->
+        viewModel.onExistingSiteSelected()
         advanceUntilIdle()

-        assertThat(events.last()).isEqualTo(GoToExistingDomain)
+        assertThat(events.last()).isEqualTo(GoToExistingSite(domain))
     }

     @Test


Fix Null Pointer Exception on getSelectedSite

Fixes #19520
Sentry [JETPACK-ANDROID-E2J](https://a8c.sentry.io/issues/4592008344/?referrer=github_integration)

This PR prevents a crash when siteRepository.getSelectedSite returns a null. If there is no site, we can continue.
Note: I could not reproduce this crash, so this a preventative measure

**To test:**
- Install the app
- Login and select a site
- Navigate to My Site > More
- ✅ Verify the more menu is shown
- Navigate around to different options and verify that the menu continues to respond

## Regression Notes
1. Potential unintended areas of impact
The app crashes when the more menu is accessed

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

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

PR submission checklist:
- [X] I have completed the Regression Notes.
- [X] I have considered adding accessibility improvements for my changes.
- [X] I have considered if this change warrants user-facing release notes and have added them to `RELEASE-NOTES.txt` if necessary.

UI Changes testing checklist: N/A

NullPointerException

NullPointerException
Sentry Issue: [JETPACK-ANDROID-E2J](https://a8c.sentry.io/issues/4592008344/?referrer=github_integration)

```
NullPointerException: None
    at org.wordpress.android.ui.mysite.menu.MenuViewModel.start(MenuViewModel.kt:89)
    at org.wordpress.android.ui.mysite.menu.MenuActivity$onCreate$1$1$2.invoke(MenuActivity.kt:99)
    at org.wordpress.android.ui.mysite.menu.MenuActivity$onCreate$1$1$2.invoke(MenuActivity.kt:99)
    at org.wordpress.android.ui.mysite.menu.MenuActivity$onCreate$1$1$2.invoke(MenuActivity.kt:98)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108)
...
(168 additional frame(s) were not displayed)
```

From logs, I can say the crash is related to https://github.com/wordpress-mobile/WordPress-Android/pull/19244

./gradlew :WordPress:testWordpressVanillaDebugUnitTest --tests "org.wordpress.android.ui.mysite.menu.MenuViewModelTest"

diff --git a/WordPress/src/main/java/org/wordpress/android/ui/mysite/menu/MenuActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/mysite/menu/MenuActivity.kt
index 00b4f5e1a0d0..cf26a9e7c87c 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/mysite/menu/MenuActivity.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/mysite/menu/MenuActivity.kt
@@ -107,6 +107,7 @@ class MenuActivity : AppCompatActivity() {
         viewModel.navigation.observe(this) { handleNavigationAction(it.getContentIfNotHandled()) }
         viewModel.onSnackbarMessage.observe(this) { showSnackbar(it.getContentIfNotHandled()) }
         viewModel.onQuickStartMySitePrompts.observe(this) { handleActiveTutorialPrompt(it.getContentIfNotHandled()) }
+        viewModel.onSelectedSiteMissing.observe(this) { finish() }

         // Set the Compose callback for SnackbarSequencer
         snackbarSequencer.setComposeSnackbarCallback { item ->
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/mysite/menu/MenuViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/mysite/menu/MenuViewModel.kt
index 68ddc709d560..72bd0607643f 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/mysite/menu/MenuViewModel.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/mysite/menu/MenuViewModel.kt
@@ -75,6 +75,10 @@ class MenuViewModel @Inject constructor(
     private val _snackbar = MutableSharedFlow<SnackbarMessage>()
     val snackBar = _snackbar.asSharedFlow()

+
+    private val _onSelectedSiteMissing = MutableLiveData<Unit>()
+    val onSelectedSiteMissing = _onSelectedSiteMissing as LiveData<Unit>
+
     private var quickStartEvent: QuickStartEvent? = null
     private var isStarted = false

@@ -86,7 +90,13 @@ class MenuViewModel @Inject constructor(
         if (isStarted) {
             return
         }
-        val site = selectedSiteRepository.getSelectedSite()!!
+
+        val site = selectedSiteRepository.getSelectedSite()
+        if (site == null) {
+            _onSelectedSiteMissing.value = Unit
+            return
+        }
+
         this.quickStartEvent = quickStartEvent
         if (quickStartEvent != null) {
             quickStartRepository.setActiveTask(quickStartEvent.task, true)

diff --git a/WordPress/src/test/java/org/wordpress/android/ui/mysite/menu/MenuViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/mysite/menu/MenuViewModelTest.kt
index 7889c673f1b3..8a0f8f58fdb1 100644
--- a/WordPress/src/test/java/org/wordpress/android/ui/mysite/menu/MenuViewModelTest.kt
+++ b/WordPress/src/test/java/org/wordpress/android/ui/mysite/menu/MenuViewModelTest.kt
@@ -165,6 +165,20 @@ class MenuViewModelTest : BaseUnitTest() {
         }
     }

+    @Test
+    fun `given vm start, when selected site is null, then site is missing event is raised`() = test {
+        whenever(selectedSiteRepository.getSelectedSite()).thenReturn(null)
+
+        val observer = mutableListOf<Unit>()
+        viewModel.onSelectedSiteMissing.observeForever { event ->
+            observer.add(event)
+        }
+
+        viewModel.start()
+
+        assert(observer.isNotEmpty())
+    }
+
     private fun findBackupListItem(items: List<MenuItemState>) =
         items.filterIsInstance(MenuItemState.MenuListItem::class.java)
             .firstOrNull { it.listItemAction == ListItemAction.BACKUP }


Stats Traffic: Today card

Fixes #19950

-----
<img width=320 src="https://github.com/wordpress-mobile/WordPress-Android/assets/990349/243c2c9b-37a6-4875-a478-6c79037fe957" />

## To Test:
Test 1:
- Enable `stats_traffic_tab` feature flag (Me -> Debug settings - Remote Features)
- Go to Stats
- `Verify` only Traffic tab, and Insights tabs are shown as in after column
- `Switch` to Traffic tab if not on it already
- `Verify` that Today card is shown in the image above
> [!NOTE]
> Traffic tab is set to show Days tab for the time being since except for the first card with graph, the rest of the cards remain same.
> Card is not hooked up to the Spinner or Date.  Waiting for that.
> Styling may need update later on

Test 2:

- Disable feature flag
- `Verify` Stats tabs all show up as in before column, and work as before
-----

## Regression Notes

1. Potential unintended areas of impact

    - Tested various combinations

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

    - Tested manually

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

    - Existing unit tests

-----

## PR Submission Checklist:

- [x] I have completed the Regression Notes.
- [x] I have considered adding accessibility improvements for my changes.
- [x] I have considered if this change warrants user-facing release notes and have added them to `RELEASE-NOTES.txt` if necessary.

-----

## UI Changes Testing Checklist:

- [ ] Portrait and landscape orientations.
- [x] 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)

Stats Traffic: By day card (Today card)

Stats Traffic: By day card (Today card)
Refer to #19973,  to reuse the card in this when date picker strip is ready

Parent: https://github.com/wordpress-mobile/jetpack-issue-repo/issues/1
![Image](https://github.com/wordpress-mobile/WordPress-iOS/assets/4062343/a00e6335-235a-4067-9bed-af657c1c9b83)

diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/OverviewUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/OverviewUseCase.kt
index 46d2a1ce3a39..ef7b192d6ba7 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/OverviewUseCase.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/OverviewUseCase.kt
@@ -14,6 +14,8 @@ import org.wordpress.android.modules.BG_THREAD
 import org.wordpress.android.modules.UI_THREAD
 import org.wordpress.android.ui.stats.refresh.lists.sections.BaseStatsUseCase
 import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem
+import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.QuickScanItem
+import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.QuickScanItem.Column
 import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.ValueItem
 import org.wordpress.android.ui.stats.refresh.lists.sections.granular.GranularUseCaseFactory
 import org.wordpress.android.ui.stats.refresh.lists.sections.granular.SelectedDateProvider
@@ -21,12 +23,14 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.granular.usecases.O
 import org.wordpress.android.ui.stats.refresh.lists.widget.WidgetUpdater.StatsWidgetUpdaters
 import org.wordpress.android.ui.stats.refresh.utils.StatsDateFormatter
 import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider
+import org.wordpress.android.ui.stats.refresh.utils.StatsUtils
 import org.wordpress.android.ui.stats.refresh.utils.toStatsSection
 import org.wordpress.android.ui.stats.refresh.utils.trackGranular
 import org.wordpress.android.util.AppLog
 import org.wordpress.android.util.AppLog.T
 import org.wordpress.android.util.LocaleManagerWrapper
 import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper
+import org.wordpress.android.util.config.StatsTrafficTabFeatureConfig
 import org.wordpress.android.viewmodel.ResourceProvider
 import java.util.Calendar
 import javax.inject.Inject
@@ -48,7 +52,9 @@ class OverviewUseCase constructor(
     private val analyticsTracker: AnalyticsTrackerWrapper,
     private val statsWidgetUpdaters: StatsWidgetUpdaters,
     private val localeManagerWrapper: LocaleManagerWrapper,
-    private val resourceProvider: ResourceProvider
+    private val resourceProvider: ResourceProvider,
+    private val statsUtils: StatsUtils,
+    private val trafficTabFeatureConfig: StatsTrafficTabFeatureConfig
 ) : BaseStatsUseCase<VisitsAndViewsModel, UiState>(
     OVERVIEW,
     mainDispatcher,
@@ -144,6 +150,120 @@ class OverviewUseCase constructor(
         domainModel: VisitsAndViewsModel,
         uiState: UiState
     ): List<BlockListItem> {
+        return if (trafficTabFeatureConfig.isEnabled()) {
+            buildTrafficOverview(domainModel, uiState)
+        } else {
+            buildGranularOverview(domainModel, uiState)
+        }
+    }
+
+    private fun buildTrafficOverview(domainModel: VisitsAndViewsModel, uiState: UiState): List<BlockListItem> {
+        val items = mutableListOf<BlockListItem>()
+        if (domainModel.dates.isNotEmpty()) {
+            val dateFromProvider = selectedDateProvider.getSelectedDate(statsGranularity)
+            val visibleBarCount = uiState.visibleBarCount ?: domainModel.dates.size
+            val availableDates = domainModel.dates.map {
+                statsDateFormatter.parseStatsDate(
+                    statsGranularity,
+                    it.period
+                )
+            }
+            val selectedDate = dateFromProvider ?: availableDates.last()
+            val index = availableDates.indexOf(selectedDate)
+
+            selectedDateProvider.selectDate(
+                selectedDate,
+                availableDates.takeLast(visibleBarCount),
+                statsGranularity
+            )
+            val selectedItem = domainModel.dates.getOrNull(index) ?: domainModel.dates.last()
+            val previousItem = domainModel.dates.getOrNull(domainModel.dates.indexOf(selectedItem) - 1)
+
+            if (statsGranularity == StatsGranularity.DAYS) {
+                buildTodayCard(selectedItem,items)
+            } else {
+                buildGranularChart(domainModel, uiState, items, selectedItem, previousItem)
+            }
+        } else {
+            selectedDateProvider.onDateLoadingFailed(statsGranularity)
+            AppLog.e(T.STATS, "There is no data to be shown in the overview block")
+        }
+        return items
+    }
+
+    private fun buildTodayCard(
+        selectedItem: VisitsAndViewsModel.PeriodData?,
+        items: MutableList<BlockListItem>
+    ) {
+        val views = selectedItem?.views ?: 0
+        val visitors = selectedItem?.visitors ?: 0
+        val likes = selectedItem?.likes ?: 0
+        val comments = selectedItem?.comments ?: 0
+
+        items.add(BlockListItem.Title(R.string.stats_timeframe_today))
+        items.add(
+            QuickScanItem(
+                Column(
+                    R.string.stats_views,
+                    statsUtils.toFormattedString(views)
+                ),
+                Column(
+                    R.string.stats_visitors,
+                    statsUtils.toFormattedString(visitors)
+                )
+            )
+        )
+
+        items.add(
+            QuickScanItem(
+                Column(
+                    R.string.stats_likes,
+                    statsUtils.toFormattedString(likes)
+                ),
+                Column(
+                    R.string.stats_comments,
+                    statsUtils.toFormattedString(comments)
+                )
+            )
+        )
+    }
+
+    private fun buildGranularChart(
+        domainModel: VisitsAndViewsModel,
+        uiState: UiState,
+        items: MutableList<BlockListItem>,
+        selectedItem: VisitsAndViewsModel.PeriodData,
+        previousItem: VisitsAndViewsModel.PeriodData?
+    ) {
+        items.add(
+            overviewMapper.buildTitle(
+                selectedItem,
+                previousItem,
+                uiState.selectedPosition,
+                isLast = selectedItem == domainModel.dates.last(),
+                statsGranularity = statsGranularity
+            )
+        )
+        items.addAll(
+            overviewMapper.buildChart(
+                domainModel.dates,
+                statsGranularity,
+                this::onBarSelected,
+                this::onBarChartDrawn,
+                uiState.selectedPosition,
+                selectedItem.period
+            )
+        )
+        items.add(
+            overviewMapper.buildColumns(
+                selectedItem,
+                this::onColumnSelected,
+                uiState.selectedPosition
+            )
+        )
+    }
+
+    private fun buildGranularOverview(domainModel: VisitsAndViewsModel, uiState: UiState): List<BlockListItem> {
         val items = mutableListOf<BlockListItem>()
         if (domainModel.dates.isNotEmpty()) {
             val dateFromProvider = selectedDateProvider.getSelectedDate(statsGranularity)
@@ -237,7 +357,9 @@ class OverviewUseCase constructor(
         private val analyticsTracker: AnalyticsTrackerWrapper,
         private val statsWidgetUpdaters: StatsWidgetUpdaters,
         private val localeManagerWrapper: LocaleManagerWrapper,
-        private val resourceProvider: ResourceProvider
+        private val resourceProvider: ResourceProvider,
+        private val statsUtils: StatsUtils,
+        private val trafficTabFeatureConfig: StatsTrafficTabFeatureConfig
     ) : GranularUseCaseFactory {
         override fun build(granularity: StatsGranularity, useCaseMode: UseCaseMode) =
             OverviewUseCase(
@@ -252,7 +374,9 @@ class OverviewUseCase constructor(
                 analyticsTracker,
                 statsWidgetUpdaters,
                 localeManagerWrapper,
-                resourceProvider
+                resourceProvider,
+                statsUtils,
+                trafficTabFeatureConfig
             )
     }
 }

diff --git a/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/OverviewUseCaseTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/OverviewUseCaseTest.kt
index 3757b6c82b41..d0206175cee8 100644
--- a/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/OverviewUseCaseTest.kt
+++ b/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/OverviewUseCaseTest.kt
@@ -35,8 +35,10 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.granular.SelectedDa
 import org.wordpress.android.ui.stats.refresh.lists.widget.WidgetUpdater.StatsWidgetUpdaters
 import org.wordpress.android.ui.stats.refresh.utils.StatsDateFormatter
 import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider
+import org.wordpress.android.ui.stats.refresh.utils.StatsUtils
 import org.wordpress.android.util.LocaleManagerWrapper
 import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper
+import org.wordpress.android.util.config.StatsTrafficTabFeatureConfig
 import org.wordpress.android.viewmodel.ResourceProvider
 import java.util.Calendar

@@ -77,6 +79,13 @@ class OverviewUseCaseTest : BaseUnitTest() {

     @Mock
     lateinit var localeManagerWrapper: LocaleManagerWrapper
+
+    @Mock
+    lateinit var statsUtils: StatsUtils
+
+    @Mock
+    lateinit var trafficTabFeatureConfig: StatsTrafficTabFeatureConfig
+
     private lateinit var useCase: OverviewUseCase
     private val site = SiteModel()
     private val siteId = 1L
@@ -100,7 +109,9 @@ class OverviewUseCaseTest : BaseUnitTest() {
             analyticsTrackerWrapper,
             statsWidgetUpdaters,
             localeManagerWrapper,
-            resourceProvider
+            resourceProvider,
+            statsUtils,
+            trafficTabFeatureConfig
         )
         site.siteId = siteId
         whenever(statsSiteProvider.siteModel).thenReturn(site)