From 076814d3691a442e3a0c21857dec7702f8359eb9 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Tue, 26 May 2026 03:44:04 -0400 Subject: [PATCH 1/2] Migrate `FetchAllDomainsUseCase` and domain management UI from FluxC to wordpress-rs Replace FluxC's `SiteStore.fetchAllDomains()` with direct wordpress-rs API call via `WpComApiClient.domains().allDomains()`. - `FetchAllDomainsUseCase` now uses wordpress-rs instead of `SiteStore` - `DomainManagementViewModel` uses `AllDomainItem` instead of `AllDomainsDomain` - `DomainsDashboardViewModel` updated for the shared `FetchAllDomainsUseCase` change - `DomainLocalSearchEngine` and `GetDomainDetailsUrl` adapted for `AllDomainItem` - Status color mapping uses `DomainListItemStatusType` instead of `StatusType` - Compose previews updated for wordpress-rs types - Bump wordpress-rs to include `Alert`/`Neutral`/`Premium` status type variants --- .../ui/domains/DomainsDashboardViewModel.kt | 44 ++++--- .../management/DomainManagementViewModel.kt | 117 +++++++++-------- .../ui/domains/management/DomainStatusRow.kt | 79 ++++++++--- .../ui/domains/management/DomainsListCard.kt | 123 +++++++++++------- .../domains/management/GetDomainDetailsUrl.kt | 24 +++- .../util/DomainLocalSearchEngine.kt | 14 +- .../usecases/FetchAllDomainsUseCase.kt | 50 +++++-- .../domains/DomainsDashboardViewModelTest.kt | 4 +- .../ui/domains/FetchAllDomainsUseCaseTest.kt | 119 +++++++++-------- .../DomainManagementViewModelTest.kt | 114 +++++++++------- .../management/GetDomainDetailsUrlTest.kt | 52 ++++---- .../management/TestDomainItemFactory.kt | 39 ++++++ .../util/DomainLocalSearchEngineTest.kt | 15 +-- 13 files changed, 501 insertions(+), 293 deletions(-) create mode 100644 WordPress/src/test/java/org/wordpress/android/ui/domains/management/TestDomainItemFactory.kt diff --git a/WordPress/src/main/java/org/wordpress/android/ui/domains/DomainsDashboardViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/domains/DomainsDashboardViewModel.kt index 950a3bdfbb0b..275a3c66487e 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/domains/DomainsDashboardViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/domains/DomainsDashboardViewModel.kt @@ -12,9 +12,9 @@ import org.wordpress.android.analytics.AnalyticsTracker.Stat.DOMAINS_DASHBOARD_V import org.wordpress.android.analytics.AnalyticsTracker.Stat.DOMAIN_CREDIT_REDEMPTION_TAPPED import org.wordpress.android.fluxc.model.PlanModel import org.wordpress.android.fluxc.model.SiteModel -import org.wordpress.android.fluxc.network.rest.wpcom.site.AllDomainsDomain import org.wordpress.android.fluxc.network.rest.wpcom.site.Domain -import org.wordpress.android.fluxc.network.rest.wpcom.site.StatusType +import uniffi.wp_api.AllDomainItem +import uniffi.wp_api.DomainListItemStatusType import org.wordpress.android.fluxc.store.SiteStore import org.wordpress.android.modules.BG_THREAD import org.wordpress.android.ui.domains.DomainsDashboardItem.AddDomain @@ -110,7 +110,7 @@ class DomainsDashboardViewModel @Inject constructor( site: SiteModel, plans: List, domains: List, - allDomains: List + allDomains: List ) { val listItems = mutableListOf() @@ -124,7 +124,7 @@ class DomainsDashboardViewModel @Inject constructor( UiStringText(freeDomainUrl), freeDomainIsPrimary, UiStringRes(R.string.active), - getStatusColor(StatusType.SUCCESS), + getStatusColor(DomainListItemStatusType.Success), UiStringRes(R.string.domains_site_domain_never_expires) ) @@ -143,10 +143,13 @@ class DomainsDashboardViewModel @Inject constructor( _uiModel.postValue(listItems) } - private fun getStatusColor(statusType: StatusType?) = when (statusType) { - StatusType.SUCCESS -> R.color.jetpack_green_50 - StatusType.NEUTRAL -> R.color.gray_50 - StatusType.WARNING -> R.color.orange_50 + private fun getStatusColor( + statusType: DomainListItemStatusType? + ) = when (statusType) { + is DomainListItemStatusType.Success, + is DomainListItemStatusType.Premium -> R.color.jetpack_green_50 + is DomainListItemStatusType.Neutral -> R.color.gray_50 + is DomainListItemStatusType.Warning -> R.color.orange_50 else -> R.color.red_50 } @@ -189,7 +192,7 @@ class DomainsDashboardViewModel @Inject constructor( private fun buildCustomDomainItems( site: SiteModel, customDomains: List, - allDomains: List + allDomains: List ): List { val listItems = mutableListOf() listItems += SiteDomainsHeader( @@ -199,15 +202,17 @@ class DomainsDashboardViewModel @Inject constructor( ) ) listItems += customDomains.map { - val allDomainsDomain = allDomains.find { allDomainsItem -> it.domain == allDomainsItem.domain } + val allDomainItem = allDomains.find { item -> + it.domain == item.domain + } SiteDomains( UiStringText(it.domain.orEmpty()), it.primaryDomain, - allDomainsDomain?.domainStatus?.status?.let { status -> - UiStringText(status) + allDomainItem?.domainStatus?.label?.let { label -> + UiStringText(label) } ?: UiStringRes(R.string.error), - getStatusColor(allDomainsDomain?.domainStatus?.statusType), + getStatusColor(allDomainItem?.domainStatus?.statusType), if (!it.hasRegistration) { null } else if (it.expirySoon) { @@ -223,19 +228,22 @@ class DomainsDashboardViewModel @Inject constructor( listOf(UiStringText(it.expiry.orEmpty())) ) }, - allDomainsDomain?.let { ListItemInteraction.create(allDomainsDomain, this::onDomainClick) } + allDomainItem?.let { + ListItemInteraction.create(allDomainItem, this::onDomainClick) + } ) } return listItems } - private fun getCleanUrl(url: String?) = StringUtils.removeTrailingSlash(UrlUtils.removeScheme(url)) + private fun getCleanUrl(url: String?) = + StringUtils.removeTrailingSlash(UrlUtils.removeScheme(url)) - private fun onDomainClick(allDomainsDomain: AllDomainsDomain) { + private fun onDomainClick(allDomainItem: AllDomainItem) { _onNavigation.value = Event( OpenDomainManagement( - allDomainsDomain.domain ?: return, - allDomainsDomain.getDomainDetailsUrl() ?: return + allDomainItem.domain.ifEmpty { return }, + allDomainItem.getDomainDetailsUrl() ?: return ) ) } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/domains/management/DomainManagementViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/domains/management/DomainManagementViewModel.kt index 04e5e857fb6a..85214ccefedd 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/domains/management/DomainManagementViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/domains/management/DomainManagementViewModel.kt @@ -16,9 +16,6 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import org.wordpress.android.R import org.wordpress.android.analytics.AnalyticsTracker.Stat -import org.wordpress.android.fluxc.network.rest.wpcom.site.AllDomainsDomain -import org.wordpress.android.fluxc.network.rest.wpcom.site.DomainStatus -import org.wordpress.android.fluxc.network.rest.wpcom.site.StatusType import org.wordpress.android.modules.UI_THREAD import org.wordpress.android.ui.domains.management.util.DomainLocalSearchEngine import org.wordpress.android.ui.domains.usecases.AllDomains @@ -28,6 +25,9 @@ import org.wordpress.android.ui.compose.theme.success import org.wordpress.android.ui.compose.theme.warning import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper import org.wordpress.android.viewmodel.ScopedViewModel +import uniffi.wp_api.AllDomainItem +import uniffi.wp_api.DomainListItemStatus +import uniffi.wp_api.DomainListItemStatusType import java.time.LocalDate import java.time.ZoneId import java.util.Date @@ -44,7 +44,8 @@ class DomainManagementViewModel @Inject constructor( private val _actionEvents = MutableSharedFlow() val actionEvents: Flow = _actionEvents - private val _uiStateFlow: MutableStateFlow = MutableStateFlow(UiState.PopulatedList.Initial) + private val _uiStateFlow: MutableStateFlow = + MutableStateFlow(UiState.PopulatedList.Initial) val uiStateFlow = _uiStateFlow.asStateFlow() private val searchQuery = MutableStateFlow("") @@ -66,7 +67,8 @@ class DomainManagementViewModel @Inject constructor( _uiStateFlow.value = when (it) { AllDomains.Empty -> UiState.Empty AllDomains.Error -> UiState.Error - is AllDomains.Success -> UiState.PopulatedList.Loaded.Complete(it.domains) + is AllDomains.Success -> + UiState.PopulatedList.Loaded.Complete(it.domains) } } } @@ -119,102 +121,107 @@ class DomainManagementViewModel @Inject constructor( } sealed class ActionEvent { - data class DomainTapped(val domain: String, val detailUrl: String): ActionEvent() - object AddDomainTapped: ActionEvent() - object NavigateBackTapped: ActionEvent() + data class DomainTapped( + val domain: String, + val detailUrl: String + ) : ActionEvent() + object AddDomainTapped : ActionEvent() + object NavigateBackTapped : ActionEvent() } sealed class UiState { - sealed class PopulatedList: UiState() { - object Initial: PopulatedList() + sealed class PopulatedList : UiState() { + object Initial : PopulatedList() sealed class Loaded : PopulatedList() { - abstract val allDomains: List + abstract val allDomains: List data class Complete( - override val allDomains: List - ): Loaded() + override val allDomains: List + ) : Loaded() data class Filtered( - override val allDomains: List, - val filtered: List + override val allDomains: List, + val filtered: List ) : Loaded() } } - object Empty: UiState() - object Error: UiState() + object Empty : UiState() + object Error : UiState() } } sealed class DomainCardUiState { - object Initial: DomainCardUiState() + object Initial : DomainCardUiState() data class Loaded( - val domain: String?, - val title: String?, + val domain: String, + val title: String, val detailUrl: String?, val statusUiState: StatusRowUiState, - ): DomainCardUiState() + ) : DomainCardUiState() companion object { @Composable - fun fromDomain(domain: AllDomainsDomain?) = (domain ?: AllDomainsDomain()).let { - val domainStatus = it.domainStatus ?: DomainStatus() - Loaded( - domain = it.domain, - title = it.blogName, - detailUrl = it.getDomainDetailsUrl(), - statusUiState = StatusRowUiState.Loaded( - indicatorColor = domainStatus.indicatorColor, - statusText = domainStatus.statusText, - textColor = domainStatus.textColor, - isBold = domainStatus.isBold, - expiry = it.expiry?.toLocalDate(), - ) + fun fromDomain(domain: AllDomainItem) = Loaded( + domain = domain.domain, + title = domain.blogName, + detailUrl = domain.getDomainDetailsUrl(), + statusUiState = StatusRowUiState.Loaded( + indicatorColor = domain.domainStatus.indicatorColor, + statusText = domain.domainStatus.statusText, + textColor = domain.domainStatus.textColor, + isBold = domain.domainStatus.isBold, + expiry = domain.expiry?.toLocalDate(), ) - } + ) } } sealed class StatusRowUiState { - object Initial: StatusRowUiState() + object Initial : StatusRowUiState() data class Loaded( val indicatorColor: Color, val statusText: String, val textColor: Color, val isBold: Boolean = false, val expiry: LocalDate?, - ): StatusRowUiState() + ) : StatusRowUiState() } private fun Date.toLocalDate(zoneId: ZoneId = ZoneId.systemDefault()) = toInstant().atZone(zoneId).toLocalDate() -val DomainStatus.indicatorColor + +val DomainListItemStatus.indicatorColor @Composable get() = when (statusType) { - StatusType.SUCCESS -> MaterialTheme.colorScheme.success - StatusType.NEUTRAL -> MaterialTheme.colorScheme.neutral - StatusType.ALERT -> MaterialTheme.colorScheme.error - StatusType.WARNING -> MaterialTheme.colorScheme.warning - StatusType.ERROR -> MaterialTheme.colorScheme.error - StatusType.UNKNOWN -> MaterialTheme.colorScheme.error - null -> MaterialTheme.colorScheme.error + is DomainListItemStatusType.Success, + is DomainListItemStatusType.Premium -> + MaterialTheme.colorScheme.success + is DomainListItemStatusType.Neutral -> + MaterialTheme.colorScheme.neutral + is DomainListItemStatusType.Alert -> + MaterialTheme.colorScheme.error + is DomainListItemStatusType.Warning -> + MaterialTheme.colorScheme.warning + is DomainListItemStatusType.Error, + is DomainListItemStatusType.Other -> + MaterialTheme.colorScheme.error } -val DomainStatus.statusText +val DomainListItemStatus.statusText @Composable - get() = status ?: stringResource(id = R.string.error) + get() = label.ifEmpty { stringResource(id = R.string.error) } -val DomainStatus.textColor +val DomainListItemStatus.textColor @Composable get() = when (statusType) { - StatusType.ERROR, - StatusType.UNKNOWN, - null -> MaterialTheme.colorScheme.error + is DomainListItemStatusType.Error, + is DomainListItemStatusType.Other -> + MaterialTheme.colorScheme.error else -> LocalTextStyle.current.color } -val DomainStatus.isBold +val DomainListItemStatus.isBold @Composable get() = when (statusType) { - StatusType.ERROR, - StatusType.UNKNOWN, - null -> true + is DomainListItemStatusType.Error, + is DomainListItemStatusType.Other -> true else -> false } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/domains/management/DomainStatusRow.kt b/WordPress/src/main/java/org/wordpress/android/ui/domains/management/DomainStatusRow.kt index 6829669fc07a..b69dc145f6dd 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/domains/management/DomainStatusRow.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/domains/management/DomainStatusRow.kt @@ -28,10 +28,11 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp import org.wordpress.android.R -import org.wordpress.android.fluxc.network.rest.wpcom.site.DomainStatus -import org.wordpress.android.fluxc.network.rest.wpcom.site.StatusType import org.wordpress.android.ui.domains.management.composable.PendingGhostStrip import org.wordpress.android.ui.compose.theme.AppThemeM3 +import uniffi.wp_api.DomainListItemStatus +import uniffi.wp_api.DomainListItemStatusId +import uniffi.wp_api.DomainListItemStatusType import java.time.LocalDate import java.time.format.DateTimeFormatter import java.time.format.FormatStyle @@ -58,7 +59,9 @@ fun StatusRow( text = uiState.statusText, color = uiState.textColor, style = if (uiState.isBold) { - LocalTextStyle.current.copy(fontWeight = FontWeight.Bold) + LocalTextStyle.current.copy( + fontWeight = FontWeight.Bold + ) } else { LocalTextStyle.current }, @@ -66,7 +69,10 @@ fun StatusRow( Spacer(modifier = Modifier.weight(1f)) uiState.expiry?.let { localDate -> Text( - text = stringResource(R.string.domain_management_expires, localDate.mediumFormat), + text = stringResource( + R.string.domain_management_expires, + localDate.mediumFormat + ), textAlign = TextAlign.End, color = MaterialTheme.colorScheme.outline, ) @@ -89,30 +95,72 @@ fun BulletPoint( ) private val LocalDate.mediumFormat - get() = format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)) + get() = format( + DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM) + ) +private val previewStatusTypes = listOf( + DomainListItemStatusType.Success, + DomainListItemStatusType.Warning, + DomainListItemStatusType.Error, + DomainListItemStatusType.Alert, + DomainListItemStatusType.Neutral, + DomainListItemStatusType.Premium, +) -class PreviewStatusProvider: PreviewParameterProvider?> { +class PreviewStatusProvider : + PreviewParameterProvider?> { override val values - get() = StatusType.values().asSequence() - .map { + get() = previewStatusTypes.asSequence() + .map { statusType -> Pair( - DomainStatus(it.titleName, it), - LocalDate.of(2024,8,15), + DomainListItemStatus( + id = DomainListItemStatusId.Active, + label = statusType.previewLabel, + statusType = statusType, + cta = null, + ), + LocalDate.of(2024, 8, 15), ) } + - sequenceOf(Pair(DomainStatus(), null)) + + sequenceOf( + Pair( + DomainListItemStatus( + id = DomainListItemStatusId.Active, + label = "", + statusType = DomainListItemStatusType.Success, + cta = null, + ), + null + ) + ) + sequenceOf(null) } +private val DomainListItemStatusType.previewLabel: String + get() = when (this) { + is DomainListItemStatusType.Success -> "Success" + is DomainListItemStatusType.Warning -> "Warning" + is DomainListItemStatusType.Error -> "Error" + is DomainListItemStatusType.Alert -> "Alert" + is DomainListItemStatusType.Neutral -> "Neutral" + is DomainListItemStatusType.Premium -> "Premium" + is DomainListItemStatusType.Other -> "Other" + } + @Preview(showBackground = true, widthDp = 296) -@Preview(showBackground = true, widthDp = 296, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Preview( + showBackground = true, + widthDp = 296, + uiMode = Configuration.UI_MODE_NIGHT_YES +) @Composable fun DomainStatusRowPreview( - @PreviewParameter(PreviewStatusProvider::class) status: Pair?, + @PreviewParameter(PreviewStatusProvider::class) + status: Pair?, ) { AppThemeM3 { - Column (Modifier.padding(8.dp)) { + Column(Modifier.padding(8.dp)) { StatusRow( uiState = status?.let { (domainStatus, expiry) -> StatusRowUiState.Loaded( @@ -130,6 +178,3 @@ fun DomainStatusRowPreview( } } } - -val StatusType.titleName - get() = name.lowercase().replaceFirstChar(Char::titlecase) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/domains/management/DomainsListCard.kt b/WordPress/src/main/java/org/wordpress/android/ui/domains/management/DomainsListCard.kt index aa8699fd074d..3b6329c5fee2 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/domains/management/DomainsListCard.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/domains/management/DomainsListCard.kt @@ -23,11 +23,14 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import org.wordpress.android.R -import org.wordpress.android.fluxc.network.rest.wpcom.site.AllDomainsDomain -import org.wordpress.android.fluxc.network.rest.wpcom.site.DomainStatus -import org.wordpress.android.fluxc.network.rest.wpcom.site.StatusType import org.wordpress.android.ui.domains.management.composable.PendingGhostStrip import org.wordpress.android.ui.compose.theme.AppThemeM3 +import uniffi.wp_api.AllDomainItem +import uniffi.wp_api.DomainListItemStatus +import uniffi.wp_api.DomainListItemStatusId +import uniffi.wp_api.DomainListItemStatusType +import uniffi.wp_api.DomainSubtype +import uniffi.wp_api.DomainSubtypeId import java.time.LocalDate import java.time.ZoneId import java.util.Date @@ -36,14 +39,20 @@ import java.util.Date @Composable fun DomainListCard( uiState: DomainCardUiState, - onDomainTapped: (domain: String, detailUrl: String) -> Unit = { _: String, _: String -> }, - ) { + onDomainTapped: (domain: String, detailUrl: String) -> Unit = + { _: String, _: String -> }, +) { OutlinedCard( modifier = Modifier.fillMaxWidth(), - border = BorderStroke(1.dp, MaterialTheme.colorScheme.outlineVariant), + border = BorderStroke( + 1.dp, + MaterialTheme.colorScheme.outlineVariant + ), onClick = { - if (uiState is DomainCardUiState.Loaded && uiState.detailUrl != null) { - onDomainTapped(uiState.domain ?: "", uiState.detailUrl) + if (uiState is DomainCardUiState.Loaded && + uiState.detailUrl != null + ) { + onDomainTapped(uiState.domain, uiState.detailUrl) } }, ) { @@ -68,19 +77,15 @@ fun DomainListCard( ) } is DomainCardUiState.Loaded -> { - uiState.domain?.let { domain -> - Text( - text = domain, - style = MaterialTheme.typography.bodyLarge, - ) - } + Text( + text = uiState.domain, + style = MaterialTheme.typography.bodyLarge, + ) Spacer(modifier = Modifier.height(4.dp)) - uiState.title?.let { - Text( - text = it, - color = MaterialTheme.colorScheme.outline, - ) - } + Text( + text = uiState.title, + color = MaterialTheme.colorScheme.outline, + ) Spacer(modifier = Modifier.height(8.dp)) StatusRow( uiState = uiState.statusUiState, @@ -94,8 +99,12 @@ fun DomainListCard( Spacer(modifier = Modifier.width(24.dp)) } else { Icon( - painter = painterResource(id = R.drawable.ic_chevron_right_white_24dp), - contentDescription = stringResource(R.string.domain_management_open_domain_details), + painter = painterResource( + id = R.drawable.ic_chevron_right_white_24dp + ), + contentDescription = stringResource( + R.string.domain_management_open_domain_details + ), tint = MaterialTheme.colorScheme.outline, ) } @@ -104,10 +113,14 @@ fun DomainListCard( } @Preview(showBackground = true, widthDp = 360) -@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Preview( + showBackground = true, + widthDp = 360, + uiMode = Configuration.UI_MODE_NIGHT_YES +) @Composable fun DomainListCardPreview() { - val expiry = LocalDate.of(2024,8,15).asLegacyDate() + val expiry = LocalDate.of(2024, 8, 15).asLegacyDate() AppThemeM3 { Column( @@ -117,30 +130,18 @@ fun DomainListCardPreview() { DomainListCard(uiState = DomainCardUiState.Initial) DomainListCard( uiState = DomainCardUiState.fromDomain( - domain = AllDomainsDomain( - domain = "domain.cool", - blogName = "A cool website", - domainStatus = DomainStatus(StatusType.SUCCESS.titleName, StatusType.SUCCESS), + domain = previewDomain( + status = DomainListItemStatusType.Success, + statusLabel = "Active", expiry = expiry, ) ) ) DomainListCard( uiState = DomainCardUiState.fromDomain( - domain = AllDomainsDomain( - domain = "domain.cool", - blogName = "A cool website", - domainStatus = DomainStatus(StatusType.ERROR.titleName, StatusType.ERROR), - expiry = expiry, - ) - ) - ) - DomainListCard( - uiState = DomainCardUiState.fromDomain( - domain = AllDomainsDomain( - domain = "domain.cool", - blogName = "A cool website", - domainStatus = null, + domain = previewDomain( + status = DomainListItemStatusType.Error, + statusLabel = "Expired", expiry = expiry, ) ) @@ -149,5 +150,39 @@ fun DomainListCardPreview() { } } -private fun LocalDate.asLegacyDate(zoneId: ZoneId = ZoneId.systemDefault()) = - Date.from(atStartOfDay(zoneId).toInstant()) +@Suppress("LongParameterList") +private fun previewDomain( + domain: String = "domain.cool", + blogName: String = "A cool website", + status: DomainListItemStatusType = DomainListItemStatusType.Success, + statusLabel: String = "Active", + expiry: Date? = null, +) = AllDomainItem( + domain = domain, + subtype = DomainSubtype( + id = DomainSubtypeId.DomainRegistration, + label = "Domain name registration" + ), + blogId = 0u, + blogName = blogName, + siteSlug = "domain.wordpress.com", + autoRenewing = false, + currentUserIsOwner = true, + isDomainOnlySite = false, + expiry = expiry, + expired = false, + primaryDomain = true, + canSetAsPrimary = true, + domainStatus = DomainListItemStatus( + id = DomainListItemStatusId.Active, + label = statusLabel, + statusType = status, + cta = null, + ), + subscriptionId = null, + tags = emptyList(), +) + +private fun LocalDate.asLegacyDate( + zoneId: ZoneId = ZoneId.systemDefault() +) = Date.from(atStartOfDay(zoneId).toInstant()) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/domains/management/GetDomainDetailsUrl.kt b/WordPress/src/main/java/org/wordpress/android/ui/domains/management/GetDomainDetailsUrl.kt index e157c053e1b6..3c0fcee0af84 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/domains/management/GetDomainDetailsUrl.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/domains/management/GetDomainDetailsUrl.kt @@ -1,10 +1,22 @@ package org.wordpress.android.ui.domains.management -import org.wordpress.android.fluxc.network.rest.wpcom.site.AllDomainsDomain +import uniffi.wp_api.AllDomainItem +import uniffi.wp_api.DomainSubtypeId -fun AllDomainsDomain.getDomainDetailsUrl(): String? = - if (domain.isNullOrEmpty() || siteSlug.isNullOrEmpty()) null else when (type) { - "transfer" -> "https://wordpress.com/domains/manage/all/$domain/transfer/in/$siteSlug" - "redirect" -> "https://wordpress.com/domains/manage/all/$domain/redirect/$siteSlug" - else -> "https://wordpress.com/domains/manage/all/$domain/edit/$siteSlug" +private const val REDIRECT_SUBTYPE = "redirect" + +fun AllDomainItem.getDomainDetailsUrl(): String? { + if (domain.isEmpty() || siteSlug.isEmpty()) return null + return when (subtype.id) { + is DomainSubtypeId.DomainTransfer -> + "https://wordpress.com/domains/manage/all/$domain/transfer/in/$siteSlug" + is DomainSubtypeId.Other -> + if ((subtype.id as DomainSubtypeId.Other).v1 == REDIRECT_SUBTYPE) { + "https://wordpress.com/domains/manage/all/$domain/redirect/$siteSlug" + } else { + "https://wordpress.com/domains/manage/all/$domain/edit/$siteSlug" + } + else -> + "https://wordpress.com/domains/manage/all/$domain/edit/$siteSlug" } +} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/domains/management/util/DomainLocalSearchEngine.kt b/WordPress/src/main/java/org/wordpress/android/ui/domains/management/util/DomainLocalSearchEngine.kt index 3337dbb0d4c2..8f569b82ae9a 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/domains/management/util/DomainLocalSearchEngine.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/domains/management/util/DomainLocalSearchEngine.kt @@ -1,15 +1,15 @@ package org.wordpress.android.ui.domains.management.util -import org.wordpress.android.fluxc.network.rest.wpcom.site.AllDomainsDomain +import uniffi.wp_api.AllDomainItem import javax.inject.Inject class DomainLocalSearchEngine @Inject constructor() { - fun filter(domains: List, query: String): List = + fun filter(domains: List, query: String): List = domains.filter { it.matches(query) } - private fun AllDomainsDomain.matches(query: String) = - domain?.contains(query, true) ?: false - || siteSlug?.contains(query, true) ?: false - || blogName?.contains(query, true) ?: false - || domainStatus?.status?.contains(query, true) ?: false + private fun AllDomainItem.matches(query: String) = + domain.contains(query, true) + || siteSlug.contains(query, true) + || blogName.contains(query, true) + || domainStatus.label.contains(query, true) } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/domains/usecases/FetchAllDomainsUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/domains/usecases/FetchAllDomainsUseCase.kt index 4f7c6f50e261..1a7158175a11 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/domains/usecases/FetchAllDomainsUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/domains/usecases/FetchAllDomainsUseCase.kt @@ -1,30 +1,56 @@ package org.wordpress.android.ui.domains.usecases -import org.wordpress.android.fluxc.network.rest.wpcom.site.AllDomainsDomain -import org.wordpress.android.fluxc.store.SiteStore +import org.wordpress.android.fluxc.store.AccountStore +import org.wordpress.android.networking.restapi.WpComApiClientProvider import org.wordpress.android.util.AppLog +import rs.wordpress.api.kotlin.WpComApiClient +import rs.wordpress.api.kotlin.WpRequestResult +import uniffi.wp_api.AllDomainItem +import uniffi.wp_api.AllDomainsParams import javax.inject.Inject -class FetchAllDomainsUseCase @Inject constructor( - private val siteStore: SiteStore, +class FetchAllDomainsUseCase @Inject constructor( + private val wpComApiClientProvider: WpComApiClientProvider, + private val accountStore: AccountStore, ) { + private var wpComApiClient: WpComApiClient? = null + + @Synchronized + private fun getOrCreateClient(): WpComApiClient { + val token = requireNotNull(accountStore.accessToken) { + "WP.com access token is required" + } + return wpComApiClient + ?: wpComApiClientProvider.getWpComApiClient(token) + .also { wpComApiClient = it } + } + suspend fun execute(): AllDomains { - val result = siteStore.fetchAllDomains() - return when { - result.isError -> { - AppLog.e(AppLog.T.API, "An error occurred while fetching all domains: ${result.error.message}") + val result = getOrCreateClient() + .request { it.domains().allDomains(AllDomainsParams()).data } + return when (result) { + is WpRequestResult.Success -> { + val domains = result.response.domains + if (domains.isEmpty()) { + AllDomains.Empty + } else { + AllDomains.Success(domains) + } + } + else -> { + AppLog.e( + AppLog.T.API, + "An error occurred while fetching all domains" + ) AllDomains.Error } - - result.domains.isNullOrEmpty() -> AllDomains.Empty - else -> AllDomains.Success(requireNotNull(result.domains)) } } } sealed interface AllDomains { data class Success( - val domains: List, + val domains: List, ) : AllDomains data object Empty : AllDomains diff --git a/WordPress/src/test/java/org/wordpress/android/ui/domains/DomainsDashboardViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/domains/DomainsDashboardViewModelTest.kt index 7f2743effab4..177cb120031c 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/domains/DomainsDashboardViewModelTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/domains/DomainsDashboardViewModelTest.kt @@ -10,8 +10,8 @@ import org.wordpress.android.BaseUnitTest import org.wordpress.android.R import org.wordpress.android.fluxc.model.PlanModel import org.wordpress.android.fluxc.model.SiteModel -import org.wordpress.android.fluxc.network.rest.wpcom.site.AllDomainsDomain import org.wordpress.android.fluxc.network.rest.wpcom.site.Domain +import org.wordpress.android.ui.domains.management.testDomainItem import org.wordpress.android.fluxc.store.SiteStore import org.wordpress.android.fluxc.store.SiteStore.FetchedDomainsPayload import org.wordpress.android.fluxc.store.SiteStore.OnPlansFetched @@ -196,7 +196,7 @@ class DomainsDashboardViewModelTest : BaseUnitTest() { wpcomDomain = false ) - private val allDomainsDomain = AllDomainsDomain(domain = "henna.tattoo") + private val allDomainsDomain = testDomainItem(domain = "henna.tattoo") private val siteWithFreePlan = SiteModel().apply { siteId = TEST_SITE_ID diff --git a/WordPress/src/test/java/org/wordpress/android/ui/domains/FetchAllDomainsUseCaseTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/domains/FetchAllDomainsUseCaseTest.kt index b5d449e511e7..1b09a1c81cb6 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/domains/FetchAllDomainsUseCaseTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/domains/FetchAllDomainsUseCaseTest.kt @@ -1,89 +1,106 @@ package org.wordpress.android.ui.domains import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat 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.whenever import org.wordpress.android.BaseUnitTest -import org.wordpress.android.fluxc.network.rest.wpcom.site.AllDomainsDomain -import org.wordpress.android.fluxc.store.SiteStore +import org.wordpress.android.fluxc.store.AccountStore +import org.wordpress.android.networking.restapi.WpComApiClientProvider +import org.wordpress.android.ui.domains.management.testDomainItem import org.wordpress.android.ui.domains.usecases.AllDomains import org.wordpress.android.ui.domains.usecases.FetchAllDomainsUseCase +import rs.wordpress.api.kotlin.WpComApiClient +import rs.wordpress.api.kotlin.WpRequestResult +import uniffi.wp_api.AllDomainsResponse +import uniffi.wp_api.RequestMethod @ExperimentalCoroutinesApi @RunWith(MockitoJUnitRunner::class) -class FetchAllDomainsUseCaseTest: BaseUnitTest() { +class FetchAllDomainsUseCaseTest : BaseUnitTest() { @Mock - lateinit var store: SiteStore + lateinit var wpComApiClientProvider: WpComApiClientProvider - private lateinit var fetchAllDomainsUseCase: FetchAllDomainsUseCase + @Mock + lateinit var accountStore: AccountStore + + @Mock + lateinit var wpComApiClient: WpComApiClient + + private lateinit var useCase: FetchAllDomainsUseCase @Before fun setUp() { - fetchAllDomainsUseCase = FetchAllDomainsUseCase(store) + whenever(accountStore.accessToken).thenReturn("test-token") + whenever(wpComApiClientProvider.getWpComApiClient("test-token")) + .thenReturn(wpComApiClient) + useCase = FetchAllDomainsUseCase(wpComApiClientProvider, accountStore) } + @Suppress("UNCHECKED_CAST") @Test - fun `given all-domains call returns results, when the usecase is execute, returns success`() = runTest { - whenever(store.fetchAllDomains()).thenReturn( - SiteStore.FetchedAllDomainsPayload( - listOf( - AllDomainsDomain("test.domain.one"), - AllDomainsDomain("test.domain.two") - ) + fun `given all-domains returns results, when execute, returns success`() = + test { + val domains = listOf( + testDomainItem(domain = "test.domain.one"), + testDomainItem(domain = "test.domain.two"), ) - ) + whenever(wpComApiClient.request(any())) + .thenReturn( + WpRequestResult.Success( + AllDomainsResponse(domains) + ) as WpRequestResult + ) - val result = fetchAllDomainsUseCase.execute() + val result = useCase.execute() - assertThat(result is AllDomains.Success).isTrue - with(result as AllDomains.Success) { - assertThat(domains.size).isEqualTo(2) - assertThat(domains[0].domain).isEqualTo("test.domain.one") - assertThat(domains[1].domain).isEqualTo("test.domain.two") + assertThat(result).isInstanceOf(AllDomains.Success::class.java) + with(result as AllDomains.Success) { + assertThat(this.domains).hasSize(2) + assertThat(this.domains[0].domain) + .isEqualTo("test.domain.one") + assertThat(this.domains[1].domain) + .isEqualTo("test.domain.two") + } } - } + @Suppress("UNCHECKED_CAST") @Test - fun `given the all-domain call returns error, when usecase is execute, returns error`() = runTest { - whenever(store.fetchAllDomains()).thenReturn( - SiteStore.FetchedAllDomainsPayload( - SiteStore.AllDomainsError( - SiteStore.AllDomainsErrorType.GENERIC_ERROR, - null + fun `given all-domains returns error, when execute, returns error`() = + test { + whenever(wpComApiClient.request(any())) + .thenReturn( + WpRequestResult.UnknownError( + 500.toUInt(), + "Internal Server Error", + "", + RequestMethod.GET + ) ) - ) - ) - - val result = fetchAllDomainsUseCase.execute() - assertThat(result is AllDomains.Error).isTrue - } - - @Test - fun `given the all-domain call returns empty response, when usecase execute, returns empty`() = runTest { - whenever(store.fetchAllDomains()).thenReturn( - SiteStore.FetchedAllDomainsPayload(listOf()) - ) + val result = useCase.execute() - val result = fetchAllDomainsUseCase.execute() - - assertThat(result is AllDomains.Empty).isTrue - } + assertThat(result).isInstanceOf(AllDomains.Error::class.java) + } + @Suppress("UNCHECKED_CAST") @Test - fun `given the all-domain call returns null response, when usecase execute, returns empty`() = runTest { - whenever(store.fetchAllDomains()).thenReturn( - SiteStore.FetchedAllDomainsPayload(null) - ) + fun `given all-domains returns empty, when execute, returns empty`() = + test { + whenever(wpComApiClient.request(any())) + .thenReturn( + WpRequestResult.Success( + AllDomainsResponse(emptyList()) + ) as WpRequestResult + ) - val result = fetchAllDomainsUseCase.execute() + val result = useCase.execute() - assertThat(result is AllDomains.Empty).isTrue - } + assertThat(result).isInstanceOf(AllDomains.Empty::class.java) + } } diff --git a/WordPress/src/test/java/org/wordpress/android/ui/domains/management/DomainManagementViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/domains/management/DomainManagementViewModelTest.kt index 356ec5976c2c..6565154564cd 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/domains/management/DomainManagementViewModelTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/domains/management/DomainManagementViewModelTest.kt @@ -15,11 +15,6 @@ import org.mockito.kotlin.verifyNoInteractions import org.mockito.kotlin.whenever import org.wordpress.android.BaseUnitTest import org.wordpress.android.analytics.AnalyticsTracker.Stat.DOMAIN_MANAGEMENT_DOMAINS_LIST_SHOWN -import org.wordpress.android.fluxc.network.rest.wpcom.site.AllDomainsDomain -import org.wordpress.android.fluxc.store.SiteStore -import org.wordpress.android.fluxc.store.SiteStore.AllDomainsError -import org.wordpress.android.fluxc.store.SiteStore.AllDomainsErrorType -import org.wordpress.android.fluxc.store.SiteStore.FetchedAllDomainsPayload import org.wordpress.android.ui.domains.management.DomainManagementViewModel.ActionEvent import org.wordpress.android.ui.domains.management.DomainManagementViewModel.UiState import org.wordpress.android.ui.domains.management.util.DomainLocalSearchEngine @@ -35,9 +30,6 @@ class DomainManagementViewModelTest : BaseUnitTest() { @Mock lateinit var useCase: FetchAllDomainsUseCase - @Mock - lateinit var siteStore: SiteStore - @Mock lateinit var domainLocalSearchEngine: DomainLocalSearchEngine @@ -49,17 +41,21 @@ class DomainManagementViewModelTest : BaseUnitTest() { } @Test - fun `WHEN ViewModel initialized THEN track DOMAIN_MANAGEMENT_DOMAINS_LIST_SHOWN event`() = test { - initializeViewModel() - verify(analyticsTracker).track(DOMAIN_MANAGEMENT_DOMAINS_LIST_SHOWN) - } + fun `WHEN ViewModel initialized THEN track DOMAIN_MANAGEMENT_DOMAINS_LIST_SHOWN event`() = + test { + initializeViewModel() + verify(analyticsTracker).track(DOMAIN_MANAGEMENT_DOMAINS_LIST_SHOWN) + } @Test - fun `WHEN a domain is tapped THEN send DomainTapped action event`() = testWithActionEvents { events -> - viewModel.onDomainTapped(testDomain, testDomainDetailUrl) - advanceUntilIdle() - assertThat(events.last()).isEqualTo(ActionEvent.DomainTapped(testDomain, testDomainDetailUrl)) - } + fun `WHEN a domain is tapped THEN send DomainTapped action event`() = + testWithActionEvents { events -> + viewModel.onDomainTapped(testDomain, testDomainDetailUrl) + advanceUntilIdle() + assertThat(events.last()).isEqualTo( + ActionEvent.DomainTapped(testDomain, testDomainDetailUrl) + ) + } @Test fun `WHEN a navigation back button is tapped THEN send NavigateBackTapped action event`() = @@ -70,36 +66,50 @@ class DomainManagementViewModelTest : BaseUnitTest() { } @Test - fun `GIVEN empty domains result WHEN initialized THEN send empty UI state`() = testWithState { state -> - assertThat(state.last()).isEqualTo(UiState.Empty) - } + fun `GIVEN empty domains result WHEN initialized THEN send empty UI state`() = + testWithState { state -> + assertThat(state.last()).isEqualTo(UiState.Empty) + } @Test fun `GIVEN error domains result WHEN initialized THEN send error UI state`() = - testWithState(initialAllDomainsFetchResult = AllDomains.Error) { state -> + testWithState( + initialAllDomainsFetchResult = AllDomains.Error + ) { state -> assertThat(state.last()).isEqualTo(UiState.Error) } @Test fun `GIVEN successful domains result WHEN initialized THEN send populated list loaded complete UI state`() { - val domains = listOf(AllDomainsDomain(), AllDomainsDomain()) - testWithState(initialAllDomainsFetchResult = AllDomains.Success(domains)) { state -> - assertThat(state.last()).isEqualTo(UiState.PopulatedList.Loaded.Complete(domains)) + val domains = listOf(testDomainItem(), testDomainItem()) + testWithState( + initialAllDomainsFetchResult = AllDomains.Success(domains) + ) { state -> + assertThat(state.last()).isEqualTo( + UiState.PopulatedList.Loaded.Complete(domains) + ) } } @Suppress("MaxLineLength") @Test fun `GIVEN successful domains result and query is not blank WHEN search query changed THEN return populated loaded filtered UI state`() { - val domains = listOf(AllDomainsDomain(), AllDomainsDomain()) - val filteredDomains = listOf(AllDomainsDomain()) - whenever(domainLocalSearchEngine.filter(domains, query = "query")).thenReturn(filteredDomains) - - testWithState(initialAllDomainsFetchResult = AllDomains.Success(domains)) { state -> + val domains = listOf(testDomainItem(), testDomainItem()) + val filteredDomains = listOf(testDomainItem()) + whenever( + domainLocalSearchEngine.filter(domains, query = "query") + ).thenReturn(filteredDomains) + + testWithState( + initialAllDomainsFetchResult = AllDomains.Success(domains) + ) { state -> viewModel.onSearchQueryChanged("query") advanceUntilIdle() assertThat(state.last()).isEqualTo( - UiState.PopulatedList.Loaded.Filtered(allDomains = domains, filtered = filteredDomains) + UiState.PopulatedList.Loaded.Filtered( + allDomains = domains, + filtered = filteredDomains + ) ) } } @@ -107,31 +117,36 @@ class DomainManagementViewModelTest : BaseUnitTest() { @Suppress("MaxLineLength") @Test fun `GIVEN successful domains result and blank query WHEN search query changed THEN return populated loaded complete UI state`() { - val domains = listOf(AllDomainsDomain(), AllDomainsDomain()) + val domains = listOf(testDomainItem(), testDomainItem()) - testWithState(initialAllDomainsFetchResult = AllDomains.Success(domains)) { state -> + testWithState( + initialAllDomainsFetchResult = AllDomains.Success(domains) + ) { state -> viewModel.onSearchQueryChanged(" ") advanceUntilIdle() - assertThat(state.last()).isEqualTo(UiState.PopulatedList.Loaded.Complete(allDomains = domains)) + assertThat(state.last()).isEqualTo( + UiState.PopulatedList.Loaded.Complete(allDomains = domains) + ) verifyNoInteractions(domainLocalSearchEngine) } } @Test fun `onRefresh fetches all the domains again`() = test { - // Given - val useCase = FetchAllDomainsUseCase(siteStore) - whenever(siteStore.fetchAllDomains()).thenReturn( - FetchedAllDomainsPayload(AllDomainsError(AllDomainsErrorType.GENERIC_ERROR)), - FetchedAllDomainsPayload(emptyList()), + whenever(useCase.execute()).thenReturn( + AllDomains.Error, + AllDomains.Empty, + ) + viewModel = DomainManagementViewModel( + testDispatcher(), + analyticsTracker, + useCase, + domainLocalSearchEngine ) - viewModel = DomainManagementViewModel(testDispatcher(), analyticsTracker, useCase, domainLocalSearchEngine) - // When viewModel.onRefresh() - // Then - verify(siteStore, times(2)).fetchAllDomains() + verify(useCase, times(2)).execute() } private fun testWithActionEvents( @@ -140,7 +155,9 @@ class DomainManagementViewModelTest : BaseUnitTest() { ) = test { initializeViewModel(initialAllDomainsFetchResult) val actionEvents = mutableListOf() - val job = launch { viewModel.actionEvents.toList(actionEvents) } + val job = launch { + viewModel.actionEvents.toList(actionEvents) + } block(actionEvents) @@ -160,9 +177,16 @@ class DomainManagementViewModelTest : BaseUnitTest() { job.cancel() } - private suspend fun initializeViewModel(initialAllDomainsFetchResult: AllDomains = AllDomains.Empty) { + private suspend fun initializeViewModel( + initialAllDomainsFetchResult: AllDomains = AllDomains.Empty + ) { whenever(useCase.execute()).thenReturn(initialAllDomainsFetchResult) - viewModel = DomainManagementViewModel(testDispatcher(), analyticsTracker, useCase, domainLocalSearchEngine) + viewModel = DomainManagementViewModel( + testDispatcher(), + analyticsTracker, + useCase, + domainLocalSearchEngine + ) } companion object { diff --git a/WordPress/src/test/java/org/wordpress/android/ui/domains/management/GetDomainDetailsUrlTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/domains/management/GetDomainDetailsUrlTest.kt index 162e9bf01e55..569008368cd3 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/domains/management/GetDomainDetailsUrlTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/domains/management/GetDomainDetailsUrlTest.kt @@ -4,65 +4,61 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import org.assertj.core.api.Assertions.assertThat import org.junit.Test import org.wordpress.android.BaseUnitTest -import org.wordpress.android.fluxc.network.rest.wpcom.site.AllDomainsDomain +import uniffi.wp_api.DomainSubtypeId @ExperimentalCoroutinesApi class GetDomainDetailsUrlTest : BaseUnitTest() { @Test fun `WHEN a transfer domain is passed THEN a transfer detail url is generated`() { - val domain = AllDomainsDomain( + val domain = testDomainItem( domain = "transfer.domain", siteSlug = "transfer.slug", - type = "transfer" + subtypeId = DomainSubtypeId.DomainTransfer, ) - val expectedDetailUrl = "https://wordpress.com/domains/manage/all/transfer.domain/transfer/in/transfer.slug" - val actualDetailUrl = domain.getDomainDetailsUrl() - assertThat(actualDetailUrl).isEqualTo(expectedDetailUrl) + val expected = + "https://wordpress.com/domains/manage/all/transfer.domain/transfer/in/transfer.slug" + assertThat(domain.getDomainDetailsUrl()).isEqualTo(expected) } @Test fun `WHEN a redirect domain is passed THEN a redirect detail url is generated`() { - val domain = AllDomainsDomain( + val domain = testDomainItem( domain = "redirect.domain", siteSlug = "redirect.domain.slug", - type = "redirect" + subtypeId = DomainSubtypeId.Other("redirect"), ) - val expectedDetailUrl = "https://wordpress.com/domains/manage/all/redirect.domain/redirect/redirect.domain.slug" - val actualDetailUrl = domain.getDomainDetailsUrl() - assertThat(actualDetailUrl).isEqualTo(expectedDetailUrl) + val expected = + "https://wordpress.com/domains/manage/all/redirect.domain/redirect/redirect.domain.slug" + assertThat(domain.getDomainDetailsUrl()).isEqualTo(expected) } @Test fun `WHEN a mapping domain is passed THEN the default detail url is generated`() { - val domain = AllDomainsDomain( + val domain = testDomainItem( domain = "some.domain", siteSlug = "domain.slug", - type = "mapping" + subtypeId = DomainSubtypeId.DomainConnection, ) - val expectedDetailUrl = "https://wordpress.com/domains/manage/all/some.domain/edit/domain.slug" - val actualDetailUrl = domain.getDomainDetailsUrl() - assertThat(actualDetailUrl).isEqualTo(expectedDetailUrl) + val expected = + "https://wordpress.com/domains/manage/all/some.domain/edit/domain.slug" + assertThat(domain.getDomainDetailsUrl()).isEqualTo(expected) } @Test - fun `WHEN the domain is null THEN the default detail url is null`() { - val domain = AllDomainsDomain( - domain = null, + fun `WHEN the domain is empty THEN the detail url is null`() { + val domain = testDomainItem( + domain = "", siteSlug = "domain.slug", - type = "mapping" ) - val actualDetailUrl = domain.getDomainDetailsUrl() - assertThat(actualDetailUrl).isNull() + assertThat(domain.getDomainDetailsUrl()).isNull() } @Test - fun `WHEN the slug is null THEN the default detail url is null`() { - val domain = AllDomainsDomain( + fun `WHEN the slug is empty THEN the detail url is null`() { + val domain = testDomainItem( domain = "some.domain", - siteSlug = null, - type = "mapping" + siteSlug = "", ) - val actualDetailUrl = domain.getDomainDetailsUrl() - assertThat(actualDetailUrl).isNull() + assertThat(domain.getDomainDetailsUrl()).isNull() } } diff --git a/WordPress/src/test/java/org/wordpress/android/ui/domains/management/TestDomainItemFactory.kt b/WordPress/src/test/java/org/wordpress/android/ui/domains/management/TestDomainItemFactory.kt new file mode 100644 index 000000000000..a206af229e71 --- /dev/null +++ b/WordPress/src/test/java/org/wordpress/android/ui/domains/management/TestDomainItemFactory.kt @@ -0,0 +1,39 @@ +package org.wordpress.android.ui.domains.management + +import uniffi.wp_api.AllDomainItem +import uniffi.wp_api.DomainListItemStatus +import uniffi.wp_api.DomainListItemStatusId +import uniffi.wp_api.DomainListItemStatusType +import uniffi.wp_api.DomainSubtype +import uniffi.wp_api.DomainSubtypeId + +@Suppress("LongParameterList") +fun testDomainItem( + domain: String = "", + siteSlug: String = "", + blogName: String = "", + statusLabel: String = "Active", + statusType: DomainListItemStatusType = DomainListItemStatusType.Success, + subtypeId: DomainSubtypeId = DomainSubtypeId.DomainRegistration, +) = AllDomainItem( + domain = domain, + subtype = DomainSubtype(id = subtypeId, label = ""), + blogId = 0u, + blogName = blogName, + siteSlug = siteSlug, + autoRenewing = false, + currentUserIsOwner = false, + isDomainOnlySite = false, + expiry = null, + expired = false, + primaryDomain = false, + canSetAsPrimary = false, + domainStatus = DomainListItemStatus( + id = DomainListItemStatusId.Active, + label = statusLabel, + statusType = statusType, + cta = null, + ), + subscriptionId = null, + tags = emptyList(), +) diff --git a/WordPress/src/test/java/org/wordpress/android/ui/domains/management/util/DomainLocalSearchEngineTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/domains/management/util/DomainLocalSearchEngineTest.kt index 93317d5ca245..15deffa5c8bd 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/domains/management/util/DomainLocalSearchEngineTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/domains/management/util/DomainLocalSearchEngineTest.kt @@ -2,27 +2,26 @@ package org.wordpress.android.ui.domains.management.util import org.assertj.core.api.Assertions.assertThat import org.junit.Test -import org.wordpress.android.fluxc.network.rest.wpcom.site.AllDomainsDomain -import org.wordpress.android.fluxc.network.rest.wpcom.site.DomainStatus +import org.wordpress.android.ui.domains.management.testDomainItem class DomainLocalSearchEngineTest { - private val fakeDomainFoo = AllDomainsDomain( + private val fakeDomainFoo = testDomainItem( domain = "foo.com", siteSlug = "Foo Warehouse", blogName = "Awesome blog", - domainStatus = DomainStatus(status = "Active") + statusLabel = "Active", ) - private val fakeDomainBar = AllDomainsDomain( + private val fakeDomainBar = testDomainItem( domain = "bar.com", siteSlug = "Chocolate Bar", blogName = "Sweet blog", - domainStatus = DomainStatus(status = "Activating") + statusLabel = "Activating", ) - private val fakeDomainBah = AllDomainsDomain( + private val fakeDomainBah = testDomainItem( domain = "bah.com", siteSlug = "Black sheep", blogName = "Unique blog", - domainStatus = DomainStatus(status = "Expired") + statusLabel = "Expired", ) private val allDomains = listOf(fakeDomainFoo, fakeDomainBar, fakeDomainBah) From 87f9210e6fd71a9461b2b6a18828dc583113bf13 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Tue, 26 May 2026 23:16:24 -0400 Subject: [PATCH 2/2] Use `DomainSubtypeId.SiteRedirect` for redirect domain URL generation Bump wordpress-rs to pick up the `SiteRedirect` variant and replace the `Other("redirect")` workaround in `GetDomainDetailsUrl`. --- .../ui/domains/management/GetDomainDetailsUrl.kt | 10 ++-------- .../ui/domains/management/GetDomainDetailsUrlTest.kt | 2 +- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/domains/management/GetDomainDetailsUrl.kt b/WordPress/src/main/java/org/wordpress/android/ui/domains/management/GetDomainDetailsUrl.kt index 3c0fcee0af84..456a2d206fdb 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/domains/management/GetDomainDetailsUrl.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/domains/management/GetDomainDetailsUrl.kt @@ -3,19 +3,13 @@ package org.wordpress.android.ui.domains.management import uniffi.wp_api.AllDomainItem import uniffi.wp_api.DomainSubtypeId -private const val REDIRECT_SUBTYPE = "redirect" - fun AllDomainItem.getDomainDetailsUrl(): String? { if (domain.isEmpty() || siteSlug.isEmpty()) return null return when (subtype.id) { is DomainSubtypeId.DomainTransfer -> "https://wordpress.com/domains/manage/all/$domain/transfer/in/$siteSlug" - is DomainSubtypeId.Other -> - if ((subtype.id as DomainSubtypeId.Other).v1 == REDIRECT_SUBTYPE) { - "https://wordpress.com/domains/manage/all/$domain/redirect/$siteSlug" - } else { - "https://wordpress.com/domains/manage/all/$domain/edit/$siteSlug" - } + is DomainSubtypeId.SiteRedirect -> + "https://wordpress.com/domains/manage/all/$domain/redirect/$siteSlug" else -> "https://wordpress.com/domains/manage/all/$domain/edit/$siteSlug" } diff --git a/WordPress/src/test/java/org/wordpress/android/ui/domains/management/GetDomainDetailsUrlTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/domains/management/GetDomainDetailsUrlTest.kt index 569008368cd3..abb6a48af413 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/domains/management/GetDomainDetailsUrlTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/domains/management/GetDomainDetailsUrlTest.kt @@ -25,7 +25,7 @@ class GetDomainDetailsUrlTest : BaseUnitTest() { val domain = testDomainItem( domain = "redirect.domain", siteSlug = "redirect.domain.slug", - subtypeId = DomainSubtypeId.Other("redirect"), + subtypeId = DomainSubtypeId.SiteRedirect, ) val expected = "https://wordpress.com/domains/manage/all/redirect.domain/redirect/redirect.domain.slug"