diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/model/IconResource.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/model/IconResource.kt index 29680ce69a..3342170b52 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/model/IconResource.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/model/IconResource.kt @@ -1,10 +1,12 @@ package com.x8bit.bitwarden.ui.platform.components.model +import android.os.Parcelable import androidx.annotation.DrawableRes import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.res.painterResource import com.x8bit.bitwarden.ui.platform.base.util.Text +import kotlinx.parcelize.Parcelize /** * Data class representing the resources required for an icon. @@ -23,11 +25,12 @@ data class IconResource( * @property iconRes Resource for the icon. * @property contentDescription The icon's content description. */ +@Parcelize data class IconRes( @DrawableRes val iconRes: Int, val contentDescription: Text, -) +) : Parcelable /** * A helper method to convert a list of [IconRes] to a list of [IconResource]. diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/send/SendListItem.kt b/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/send/SendListItem.kt index bdcf4ee588..e93b4eca59 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/send/SendListItem.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/send/SendListItem.kt @@ -15,10 +15,10 @@ import com.x8bit.bitwarden.ui.platform.components.BitwardenListItem import com.x8bit.bitwarden.ui.platform.components.BitwardenTwoButtonDialog import com.x8bit.bitwarden.ui.platform.components.SelectionItemData import com.x8bit.bitwarden.ui.platform.components.model.IconData +import com.x8bit.bitwarden.ui.platform.components.model.IconRes import com.x8bit.bitwarden.ui.platform.components.model.IconResource import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme import com.x8bit.bitwarden.ui.platform.util.persistentListOfNotNull -import com.x8bit.bitwarden.ui.tools.feature.send.model.SendStatusIcon import kotlinx.collections.immutable.toPersistentList /** @@ -43,7 +43,7 @@ fun SendListItem( label: String, supportingLabel: String, startIcon: IconData, - trailingLabelIcons: List, + trailingLabelIcons: List, onClick: () -> Unit, onEditClick: () -> Unit, onCopyClick: () -> Unit, diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/send/SendViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/send/SendViewModel.kt index 2b7cb29b59..c8b9173f42 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/send/SendViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/send/SendViewModel.kt @@ -18,7 +18,7 @@ import com.x8bit.bitwarden.ui.platform.base.BaseViewModel import com.x8bit.bitwarden.ui.platform.base.util.Text import com.x8bit.bitwarden.ui.platform.base.util.asText import com.x8bit.bitwarden.ui.platform.base.util.concat -import com.x8bit.bitwarden.ui.tools.feature.send.model.SendStatusIcon +import com.x8bit.bitwarden.ui.platform.components.model.IconRes import com.x8bit.bitwarden.ui.tools.feature.send.util.toViewState import com.x8bit.bitwarden.ui.vault.feature.item.VaultItemScreen import dagger.hilt.android.lifecycle.HiltViewModel @@ -350,7 +350,7 @@ data class SendState( val name: String, val deletionDate: String, val type: Type, - val iconList: List, + val iconList: List, val shareUrl: String, val hasPassword: Boolean, ) : Parcelable { diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/send/util/SendDataExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/send/util/SendDataExtensions.kt index 88edd23d6e..97a2638bd0 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/send/util/SendDataExtensions.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/send/util/SendDataExtensions.kt @@ -5,8 +5,6 @@ import com.bitwarden.core.SendView import com.x8bit.bitwarden.data.vault.repository.model.SendData import com.x8bit.bitwarden.ui.platform.util.toFormattedPattern import com.x8bit.bitwarden.ui.tools.feature.send.SendState -import com.x8bit.bitwarden.ui.tools.feature.send.model.SendStatusIcon -import java.time.Instant private const val DELETION_DATE_PATTERN: String = "MMM d, uuuu, hh:mm a" @@ -38,21 +36,7 @@ private fun List.toSendContent( SendType.TEXT -> SendState.ViewState.Content.SendItem.Type.TEXT SendType.FILE -> SendState.ViewState.Content.SendItem.Type.FILE }, - iconList = listOfNotNull( - SendStatusIcon.DISABLED.takeIf { sendView.disabled }, - SendStatusIcon.PASSWORD.takeIf { sendView.hasPassword }, - SendStatusIcon.MAX_ACCESS_COUNT_REACHED.takeIf { - sendView.maxAccessCount?.let { maxCount -> - sendView.accessCount >= maxCount - } == true - }, - SendStatusIcon.EXPIRED.takeIf { - sendView.expirationDate?.isBefore(Instant.now()) == true - }, - SendStatusIcon.PENDING_DELETE.takeIf { - sendView.deletionDate.isBefore(Instant.now()) - }, - ), + iconList = sendView.toLabelIcons(), shareUrl = sendView.toSendUrl(baseWebSendUrl), hasPassword = sendView.hasPassword, ) diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/send/util/SendViewExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/send/util/SendViewExtensions.kt index 7a45efabe2..5f25afeaa9 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/send/util/SendViewExtensions.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/send/util/SendViewExtensions.kt @@ -1,6 +1,24 @@ package com.x8bit.bitwarden.ui.tools.feature.send.util import com.bitwarden.core.SendView +import com.x8bit.bitwarden.ui.platform.components.model.IconRes +import com.x8bit.bitwarden.ui.tools.feature.send.model.SendStatusIcon +import java.time.Clock + +/** + * Creates the list of trailing label icons to be displayed for a [SendView]. + */ +fun SendView.toLabelIcons(clock: Clock = Clock.systemDefaultZone()): List = + listOfNotNull( + SendStatusIcon.DISABLED.takeIf { disabled }, + SendStatusIcon.PASSWORD.takeIf { hasPassword }, + SendStatusIcon.MAX_ACCESS_COUNT_REACHED.takeIf { + maxAccessCount?.let { maxCount -> accessCount >= maxCount } == true + }, + SendStatusIcon.EXPIRED.takeIf { expirationDate?.isBefore(clock.instant()) == true }, + SendStatusIcon.PENDING_DELETE.takeIf { deletionDate.isBefore(clock.instant()) }, + ) + .map { IconRes(iconRes = it.iconRes, contentDescription = it.contentDescription) } /** * Creates a sharable url from a [SendView]. diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/util/VaultItemListingDataExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/util/VaultItemListingDataExtensions.kt index a539902ba4..9afc711a58 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/util/VaultItemListingDataExtensions.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/util/VaultItemListingDataExtensions.kt @@ -10,8 +10,7 @@ import com.bitwarden.core.SendView import com.x8bit.bitwarden.R import com.x8bit.bitwarden.ui.platform.base.util.asText import com.x8bit.bitwarden.ui.platform.components.model.IconData -import com.x8bit.bitwarden.ui.platform.components.model.IconRes -import com.x8bit.bitwarden.ui.tools.feature.send.model.SendStatusIcon +import com.x8bit.bitwarden.ui.tools.feature.send.util.toLabelIcons import com.x8bit.bitwarden.ui.tools.feature.send.util.toSendUrl import com.x8bit.bitwarden.ui.vault.feature.itemlisting.VaultItemListingState import com.x8bit.bitwarden.ui.vault.feature.itemlisting.VaultItemListingsAction @@ -207,23 +206,7 @@ private fun SendView.toDisplayItem( SendType.FILE -> R.drawable.ic_send_file }, ), - extraIconList = listOfNotNull( - SendStatusIcon.DISABLED - .takeIf { disabled } - ?.let { IconRes(iconRes = it.iconRes, contentDescription = it.contentDescription) }, - SendStatusIcon.PASSWORD - .takeIf { hasPassword } - ?.let { IconRes(iconRes = it.iconRes, contentDescription = it.contentDescription) }, - SendStatusIcon.MAX_ACCESS_COUNT_REACHED - .takeIf { maxAccessCount?.let { maxCount -> accessCount >= maxCount } == true } - ?.let { IconRes(iconRes = it.iconRes, contentDescription = it.contentDescription) }, - SendStatusIcon.EXPIRED - .takeIf { expirationDate?.isBefore(clock.instant()) == true } - ?.let { IconRes(iconRes = it.iconRes, contentDescription = it.contentDescription) }, - SendStatusIcon.PENDING_DELETE - .takeIf { deletionDate.isBefore(clock.instant()) } - ?.let { IconRes(iconRes = it.iconRes, contentDescription = it.contentDescription) }, - ), + extraIconList = toLabelIcons(clock = clock), overflowOptions = listOfNotNull( VaultItemListingState.DisplayItem.OverflowItem( title = R.string.edit.asText(), diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/util/SendDataExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/util/SendDataExtensionsTest.kt index 51e974fa71..48814ec416 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/util/SendDataExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/util/SendDataExtensionsTest.kt @@ -3,6 +3,7 @@ package com.x8bit.bitwarden.ui.tools.feature.send.util import com.bitwarden.core.SendType import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSendView import com.x8bit.bitwarden.data.vault.repository.model.SendData +import com.x8bit.bitwarden.ui.platform.components.model.IconRes import com.x8bit.bitwarden.ui.tools.feature.send.SendState import com.x8bit.bitwarden.ui.tools.feature.send.model.SendStatusIcon import io.mockk.every @@ -52,6 +53,8 @@ class SendDataExtensionsTest { val sendData = SendData(list) every { textSendView.toSendUrl(DEFAULT_BASE_URL) } returns textSendViewUrl2 every { fileSendView.toSendUrl(DEFAULT_BASE_URL) } returns textSendViewUrl1 + every { textSendView.toLabelIcons(any()) } returns DEFAULT_SEND_STATUS_ICONS + every { fileSendView.toLabelIcons(any()) } returns DEFAULT_SEND_STATUS_ICONS val result = sendData.toViewState(DEFAULT_BASE_URL) @@ -65,12 +68,7 @@ class SendDataExtensionsTest { name = "mockName-1", deletionDate = "Oct 27, 2023, 12:00 PM", type = SendState.ViewState.Content.SendItem.Type.FILE, - iconList = listOf( - SendStatusIcon.PASSWORD, - SendStatusIcon.MAX_ACCESS_COUNT_REACHED, - SendStatusIcon.EXPIRED, - SendStatusIcon.PENDING_DELETE, - ), + iconList = DEFAULT_SEND_STATUS_ICONS, shareUrl = "www.test.com/#/send/mockAccessId-1/mockKey-1", hasPassword = true, ), @@ -79,12 +77,7 @@ class SendDataExtensionsTest { name = "mockName-2", deletionDate = "Oct 27, 2023, 12:00 PM", type = SendState.ViewState.Content.SendItem.Type.TEXT, - iconList = listOf( - SendStatusIcon.PASSWORD, - SendStatusIcon.MAX_ACCESS_COUNT_REACHED, - SendStatusIcon.EXPIRED, - SendStatusIcon.PENDING_DELETE, - ), + iconList = DEFAULT_SEND_STATUS_ICONS, shareUrl = "www.test.com/#/send/mockAccessId-2/mockKey-2", hasPassword = true, ), @@ -99,3 +92,26 @@ private const val SEND_VIEW_EXTENSIONS_PATH: String = "com.x8bit.bitwarden.ui.tools.feature.send.util.SendViewExtensionsKt" private const val DEFAULT_BASE_URL: String = "www.test.com/" + +private val DEFAULT_SEND_STATUS_ICONS: List = listOf( + IconRes( + iconRes = SendStatusIcon.DISABLED.iconRes, + contentDescription = SendStatusIcon.DISABLED.contentDescription, + ), + IconRes( + iconRes = SendStatusIcon.PASSWORD.iconRes, + contentDescription = SendStatusIcon.PASSWORD.contentDescription, + ), + IconRes( + iconRes = SendStatusIcon.MAX_ACCESS_COUNT_REACHED.iconRes, + contentDescription = SendStatusIcon.MAX_ACCESS_COUNT_REACHED.contentDescription, + ), + IconRes( + iconRes = SendStatusIcon.EXPIRED.iconRes, + contentDescription = SendStatusIcon.EXPIRED.contentDescription, + ), + IconRes( + iconRes = SendStatusIcon.PENDING_DELETE.iconRes, + contentDescription = SendStatusIcon.PENDING_DELETE.contentDescription, + ), +) diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/util/SendViewExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/util/SendViewExtensionsTest.kt new file mode 100644 index 0000000000..dac4cd93c4 --- /dev/null +++ b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/util/SendViewExtensionsTest.kt @@ -0,0 +1,94 @@ +package com.x8bit.bitwarden.ui.tools.feature.send.util + +import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSendView +import com.x8bit.bitwarden.ui.platform.components.model.IconRes +import com.x8bit.bitwarden.ui.tools.feature.send.model.SendStatusIcon +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import java.time.Clock +import java.time.Instant +import java.time.ZoneOffset + +class SendViewExtensionsTest { + + private val clock: Clock = Clock.fixed( + Instant.parse("2023-10-27T12:00:00Z"), + ZoneOffset.UTC, + ) + + @Test + fun `toLabelIcons should return all of the icons when all checks have passed`() { + val sendView = createMockSendView(number = 1).copy( + // Show the password icon when true + hasPassword = true, + // Show the disabled icon when true + disabled = true, + // Show the max access count reached icon when accessCount is greater than or equal to + // the maxAccessCount + maxAccessCount = 1u, + accessCount = 1u, + // Show the pending deletion icon when the deletion date is in the past + deletionDate = Instant.parse("2023-10-26T12:00:00Z"), + // Show the expired icon when the expiration date is in the past + expirationDate = Instant.parse("2023-10-26T12:00:00Z"), + ) + + val result = sendView.toLabelIcons(clock) + + assertEquals(ALL_SEND_STATUS_ICONS, result) + } + + @Test + fun `toLabelIcons should return none of the icons when none of the checks have passed`() { + val sendView = createMockSendView(number = 1).copy( + // Hide the password icon when false + hasPassword = false, + // Hide the disabled icon when false + disabled = false, + // Hide the max access count reached icon when accessCount is less than the + // maxAccessCount + maxAccessCount = 10u, + accessCount = 1u, + // Hide the pending deletion icon when the deletion date is in the future + deletionDate = Instant.parse("2023-10-28T12:00:00Z"), + // Hide the expired icon when the expiration date is in the future + expirationDate = Instant.parse("2023-10-28T12:00:00Z"), + ) + + val result = sendView.toLabelIcons(clock) + + assertEquals(emptyList(), result) + } + + @Test + fun `toSendUrl should create an appropriate url`() { + val sendView = createMockSendView(number = 1) + + val result = sendView.toSendUrl(baseWebSendUrl = "www.test.com/") + + assertEquals("www.test.com/mockAccessId-1/mockKey-1", result) + } +} + +private val ALL_SEND_STATUS_ICONS: List = listOf( + IconRes( + iconRes = SendStatusIcon.DISABLED.iconRes, + contentDescription = SendStatusIcon.DISABLED.contentDescription, + ), + IconRes( + iconRes = SendStatusIcon.PASSWORD.iconRes, + contentDescription = SendStatusIcon.PASSWORD.contentDescription, + ), + IconRes( + iconRes = SendStatusIcon.MAX_ACCESS_COUNT_REACHED.iconRes, + contentDescription = SendStatusIcon.MAX_ACCESS_COUNT_REACHED.contentDescription, + ), + IconRes( + iconRes = SendStatusIcon.EXPIRED.iconRes, + contentDescription = SendStatusIcon.EXPIRED.contentDescription, + ), + IconRes( + iconRes = SendStatusIcon.PENDING_DELETE.iconRes, + contentDescription = SendStatusIcon.PENDING_DELETE.contentDescription, + ), +) diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/util/SendViewTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/util/SendViewTest.kt deleted file mode 100644 index d146c721f5..0000000000 --- a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/util/SendViewTest.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.x8bit.bitwarden.ui.tools.feature.send.util - -import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSendView -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test - -class SendViewTest { - - @Test - fun `toSendUrl should create an appropriate url`() { - val sendView = createMockSendView(number = 1) - - val result = sendView.toSendUrl(baseWebSendUrl = "www.test.com/") - - assertEquals("www.test.com/mockAccessId-1/mockKey-1", result) - } -}