diff --git a/app-ui-catalog/src/main/java/app/k9mail/ui/catalog/ui/atom/items/IconItems.kt b/app-ui-catalog/src/main/java/app/k9mail/ui/catalog/ui/atom/items/IconItems.kt index 240fc813639..bd508fe6e57 100644 --- a/app-ui-catalog/src/main/java/app/k9mail/ui/catalog/ui/atom/items/IconItems.kt +++ b/app-ui-catalog/src/main/java/app/k9mail/ui/catalog/ui/atom/items/IconItems.kt @@ -3,6 +3,7 @@ package app.k9mail.ui.catalog.ui.atom.items import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.grid.LazyGridScope import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -24,6 +25,28 @@ fun LazyGridScope.iconItems() { sectionHeaderItem( text = "Compose Icons", ) + sectionSubtitleItem(text = "Sizes") + defaultItem { + IconItem( + name = "Small", + imageVector = Icons.Outlined.Info, + modifier = Modifier.size(MainTheme.sizes.iconSmall), + ) + } + defaultItem { + IconItem( + name = "Default", + imageVector = Icons.Outlined.Info, + modifier = Modifier.size(MainTheme.sizes.icon), + ) + } + defaultItem { + IconItem( + name = "Large", + imageVector = Icons.Outlined.Info, + modifier = Modifier.size(MainTheme.sizes.iconLarge), + ) + } sectionSubtitleItem(text = "Filled") getIconsFor(Icons.Filled) sectionSubtitleItem(text = "Outlined") @@ -81,13 +104,13 @@ private fun IconItem( ) { Column( modifier = Modifier - .padding(defaultItemPadding()) - .then(modifier), + .padding(defaultItemPadding()), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.default), ) { Icon( imageVector = imageVector, + modifier = modifier, ) TextBodySmall(text = name) } diff --git a/core/ui/compose/designsystem/src/debug/kotlin/app/k9mail/core/ui/compose/designsystem/organism/drawer/NavigationDrawerItemBadgePreview.kt b/core/ui/compose/designsystem/src/debug/kotlin/app/k9mail/core/ui/compose/designsystem/organism/drawer/NavigationDrawerItemBadgePreview.kt index 7cd4ec5c7c9..071ce35fe5a 100644 --- a/core/ui/compose/designsystem/src/debug/kotlin/app/k9mail/core/ui/compose/designsystem/organism/drawer/NavigationDrawerItemBadgePreview.kt +++ b/core/ui/compose/designsystem/src/debug/kotlin/app/k9mail/core/ui/compose/designsystem/organism/drawer/NavigationDrawerItemBadgePreview.kt @@ -3,13 +3,25 @@ package app.k9mail.core.ui.compose.designsystem.organism.drawer import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.Preview import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes +import app.k9mail.core.ui.compose.designsystem.atom.icon.Icons @Composable @Preview(showBackground = true) internal fun NavigationDrawerItemBadgePreview() { PreviewWithThemes { NavigationDrawerItemBadge( - label = "100+", + label = "99+", + ) + } +} + +@Composable +@Preview(showBackground = true) +internal fun NavigationDrawerItemBadgeWithIconPreview() { + PreviewWithThemes { + NavigationDrawerItemBadge( + label = "99+", + imageVector = Icons.Outlined.Info, ) } } diff --git a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/icon/Icons.kt b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/icon/Icons.kt index 2bd04f6b54e..d4d712d9543 100644 --- a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/icon/Icons.kt +++ b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/icon/Icons.kt @@ -1,43 +1,67 @@ package app.k9mail.core.ui.compose.designsystem.atom.icon import androidx.compose.material.icons.automirrored.outlined.ArrowBack +import androidx.compose.material.icons.automirrored.outlined.Send import androidx.compose.material.icons.filled.Cancel import androidx.compose.material.icons.filled.CheckCircle import androidx.compose.material.icons.filled.Outbox +import androidx.compose.material.icons.filled.Star import androidx.compose.material.icons.filled.VisibilityOff import androidx.compose.material.icons.outlined.AccountCircle +import androidx.compose.material.icons.outlined.Archive import androidx.compose.material.icons.outlined.Check +import androidx.compose.material.icons.outlined.Delete +import androidx.compose.material.icons.outlined.Drafts import androidx.compose.material.icons.outlined.ErrorOutline import androidx.compose.material.icons.outlined.ExpandLess import androidx.compose.material.icons.outlined.ExpandMore +import androidx.compose.material.icons.outlined.Folder import androidx.compose.material.icons.outlined.Inbox import androidx.compose.material.icons.outlined.Info import androidx.compose.material.icons.outlined.Menu +import androidx.compose.material.icons.outlined.Report import androidx.compose.material.icons.outlined.Security +import androidx.compose.material.icons.outlined.Send import androidx.compose.material.icons.outlined.Visibility import androidx.compose.ui.graphics.vector.ImageVector +import app.k9mail.core.ui.compose.designsystem.atom.icon.filled.Dot import androidx.compose.material.icons.Icons as MaterialIcons // We're using getters so not all icons are loaded into memory as soon as one of the nested objects is accessed. object Icons { object Filled { + val Cancel: ImageVector + get() = MaterialIcons.Filled.Cancel + val CheckCircle: ImageVector get() = MaterialIcons.Filled.CheckCircle - val Cancel: ImageVector - get() = MaterialIcons.Filled.Cancel + val Dot: ImageVector + get() = MaterialIcons.Filled.Dot + + val Star: ImageVector + get() = MaterialIcons.Filled.Star } object Outlined { val AccountCircle: ImageVector get() = MaterialIcons.Outlined.AccountCircle + val Archive: ImageVector + get() = MaterialIcons.Outlined.Archive + val ArrowBack: ImageVector get() = MaterialIcons.AutoMirrored.Outlined.ArrowBack val Check: ImageVector get() = MaterialIcons.Outlined.Check + val Delete: ImageVector + get() = MaterialIcons.Outlined.Delete + + val Drafts: ImageVector + get() = MaterialIcons.Outlined.Drafts + val ErrorOutline: ImageVector get() = MaterialIcons.Outlined.ErrorOutline @@ -47,6 +71,9 @@ object Icons { val ExpandLess: ImageVector get() = MaterialIcons.Outlined.ExpandLess + val Folder: ImageVector + get() = MaterialIcons.Outlined.Folder + val Inbox: ImageVector get() = MaterialIcons.Outlined.Inbox @@ -62,6 +89,12 @@ object Icons { val Security: ImageVector get() = MaterialIcons.Outlined.Security + val Send: ImageVector + get() = MaterialIcons.AutoMirrored.Outlined.Send + + val Report: ImageVector + get() = MaterialIcons.Outlined.Report + val Visibility: ImageVector get() = MaterialIcons.Outlined.Visibility diff --git a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/icon/filled/Dot.kt b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/icon/filled/Dot.kt new file mode 100644 index 00000000000..cc85e7bf1cc --- /dev/null +++ b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/atom/icon/filled/Dot.kt @@ -0,0 +1,27 @@ +package app.k9mail.core.ui.compose.designsystem.atom.icon.filled + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.materialIcon +import androidx.compose.material.icons.materialPath +import androidx.compose.ui.graphics.vector.ImageVector + +@Suppress("MagicNumber") +val Icons.Filled.Dot: ImageVector + get() { + if (instance != null) { + return instance!! + } + instance = materialIcon(name = "Filled.Dot") { + materialPath { + moveTo(12.0f, 6.0f) + curveToRelative(-3.31f, 0.0f, -6.0f, 2.69f, -6.0f, 6.0f) + reflectiveCurveToRelative(2.69f, 6.0f, 6.0f, 6.0f) + reflectiveCurveToRelative(6.0f, -2.69f, 6.0f, -6.0f) + reflectiveCurveToRelative(-2.69f, -6.0f, -6.0f, -6.0f) + close() + } + } + return instance!! + } + +private var instance: ImageVector? = null diff --git a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/organism/drawer/NavigationDrawerItemBadge.kt b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/organism/drawer/NavigationDrawerItemBadge.kt index 97b2682fa23..d4c331080b5 100644 --- a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/organism/drawer/NavigationDrawerItemBadge.kt +++ b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/organism/drawer/NavigationDrawerItemBadge.kt @@ -1,16 +1,42 @@ package app.k9mail.core.ui.compose.designsystem.organism.drawer +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import app.k9mail.core.ui.compose.designsystem.atom.icon.Icon import app.k9mail.core.ui.compose.designsystem.atom.text.TextLabelLarge +import app.k9mail.core.ui.compose.theme2.MainTheme +/** + * A badge for a navigation drawer item with an optional icon. + * + * @param label The label to display. + * @param modifier The modifier to apply. + * @param imageVector The image vector to display (optional). + */ @Composable fun NavigationDrawerItemBadge( label: String, modifier: Modifier = Modifier, + imageVector: ImageVector? = null, ) { - TextLabelLarge( - text = label, + Row( modifier = modifier, - ) + verticalAlignment = Alignment.CenterVertically, + ) { + if (imageVector != null) { + Icon( + imageVector = imageVector, + modifier = Modifier.size(MainTheme.sizes.iconSmall) + .padding(end = MainTheme.spacings.quarter), + ) + } + TextLabelLarge( + text = label, + ) + } } diff --git a/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/ThemeSizes.kt b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/ThemeSizes.kt index 9ddb6140e87..6d4f80c92a3 100644 --- a/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/ThemeSizes.kt +++ b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/ThemeSizes.kt @@ -14,8 +14,9 @@ data class ThemeSizes( val huge: Dp, val huger: Dp, + val iconSmall: Dp, val icon: Dp, - val largeIcon: Dp, + val iconLarge: Dp, val topBarHeight: Dp, val bottomBarHeight: Dp, diff --git a/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/default/DefaultThemeSizes.kt b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/default/DefaultThemeSizes.kt index 4db563b49f6..fb84c4cfd40 100644 --- a/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/default/DefaultThemeSizes.kt +++ b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/default/DefaultThemeSizes.kt @@ -12,8 +12,9 @@ val defaultThemeSizes = ThemeSizes( huge = 256.dp, huger = 384.dp, + iconSmall = 16.dp, icon = 24.dp, - largeIcon = 32.dp, + iconLarge = 32.dp, topBarHeight = 64.dp, bottomBarHeight = 80.dp, diff --git a/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerContentPreview.kt b/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerContentPreview.kt index 0d377461317..f9cb6d53766 100644 --- a/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerContentPreview.kt +++ b/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerContentPreview.kt @@ -3,7 +3,8 @@ package app.k9mail.feature.navigation.drawer.ui import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.Preview import app.k9mail.core.ui.compose.designsystem.PreviewWithTheme -import app.k9mail.feature.navigation.drawer.ui.account.FakeData.DISPLAY_ACCOUNT +import app.k9mail.feature.navigation.drawer.ui.FakeData.DISPLAY_ACCOUNT +import kotlinx.collections.immutable.persistentListOf @Composable @Preview(showBackground = true) @@ -11,9 +12,11 @@ internal fun DrawerContentPreview() { PreviewWithTheme { DrawerContent( state = DrawerContract.State( - accounts = emptyList(), + accounts = persistentListOf(), currentAccount = null, + folders = persistentListOf(), ), + onEvent = {}, ) } } @@ -24,9 +27,10 @@ fun DrawerContentWithAccountPreview() { PreviewWithTheme { DrawerContent( state = DrawerContract.State( - accounts = listOf(DISPLAY_ACCOUNT), + accounts = persistentListOf(DISPLAY_ACCOUNT), currentAccount = DISPLAY_ACCOUNT, ), + onEvent = {}, ) } } diff --git a/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/account/FakeData.kt b/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/FakeData.kt similarity index 65% rename from feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/account/FakeData.kt rename to feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/FakeData.kt index 3bcbd017480..67061731396 100644 --- a/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/account/FakeData.kt +++ b/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/FakeData.kt @@ -1,8 +1,11 @@ -package app.k9mail.feature.navigation.drawer.ui.account +package app.k9mail.feature.navigation.drawer.ui +import app.k9mail.core.mail.folder.api.Folder +import app.k9mail.core.mail.folder.api.FolderType import app.k9mail.feature.navigation.drawer.domain.entity.DisplayAccount import app.k9mail.legacy.account.Account import app.k9mail.legacy.account.Identity +import app.k9mail.legacy.ui.folder.DisplayFolder internal object FakeData { @@ -34,4 +37,18 @@ internal object FakeData { unreadMessageCount = 0, starredMessageCount = 0, ) + + val FOLDER = Folder( + id = 1, + name = "Folder Name", + type = FolderType.REGULAR, + isLocalOnly = false, + ) + + val DISPLAY_FOLDER = DisplayFolder( + folder = FOLDER, + isInTopGroup = false, + unreadMessageCount = 14, + starredMessageCount = 5, + ) } diff --git a/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/account/AccountViewPreview.kt b/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/account/AccountViewPreview.kt index b268f894894..63595649232 100644 --- a/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/account/AccountViewPreview.kt +++ b/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/account/AccountViewPreview.kt @@ -3,9 +3,9 @@ package app.k9mail.feature.navigation.drawer.ui.account import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.Preview import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes -import app.k9mail.feature.navigation.drawer.ui.account.FakeData.DISPLAY_NAME -import app.k9mail.feature.navigation.drawer.ui.account.FakeData.EMAIL_ADDRESS -import app.k9mail.feature.navigation.drawer.ui.account.FakeData.LONG_TEXT +import app.k9mail.feature.navigation.drawer.ui.FakeData.DISPLAY_NAME +import app.k9mail.feature.navigation.drawer.ui.FakeData.EMAIL_ADDRESS +import app.k9mail.feature.navigation.drawer.ui.FakeData.LONG_TEXT @Composable @Preview(showBackground = true) @@ -15,6 +15,7 @@ internal fun AccountViewPreview() { displayName = DISPLAY_NAME, emailAddress = EMAIL_ADDRESS, accountColor = 0, + onClick = {}, ) } } @@ -27,6 +28,7 @@ internal fun AccountViewWithColorPreview() { displayName = DISPLAY_NAME, emailAddress = EMAIL_ADDRESS, accountColor = 0xFF0000, + onClick = {}, ) } } @@ -39,6 +41,7 @@ internal fun AccountViewWithLongDisplayName() { displayName = "$LONG_TEXT $DISPLAY_NAME", emailAddress = EMAIL_ADDRESS, accountColor = 0, + onClick = {}, ) } } @@ -51,6 +54,7 @@ internal fun AccountViewWithLongEmailPreview() { displayName = DISPLAY_NAME, emailAddress = "$LONG_TEXT@example.com", accountColor = 0, + onClick = {}, ) } } diff --git a/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/folder/FolderListItemBadgePreview.kt b/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/folder/FolderListItemBadgePreview.kt new file mode 100644 index 00000000000..1574f5a29ca --- /dev/null +++ b/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/folder/FolderListItemBadgePreview.kt @@ -0,0 +1,101 @@ +package app.k9mail.feature.navigation.drawer.ui.folder + +import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.Preview +import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes + +@Composable +@Preview(showBackground = true) +internal fun FolderListItemBadgePreview() { + PreviewWithThemes { + FolderListItemBadge( + unreadCount = 99, + starredCount = 0, + showStarredCount = true, + ) + } +} + +@Composable +@Preview(showBackground = true) +internal fun FolderListItemBadgeWithStarredCountPreview() { + PreviewWithThemes { + FolderListItemBadge( + unreadCount = 99, + starredCount = 1, + showStarredCount = true, + ) + } +} + +@Composable +@Preview(showBackground = true) +internal fun FolderListItemBadgeWithZeroUnreadCountPreview() { + PreviewWithThemes { + FolderListItemBadge( + unreadCount = 0, + starredCount = 1, + showStarredCount = true, + ) + } +} + +@Composable +@Preview(showBackground = true) +internal fun FolderListItemBadgeWithZeroStarredCountPreview() { + PreviewWithThemes { + FolderListItemBadge( + unreadCount = 99, + starredCount = 0, + showStarredCount = true, + ) + } +} + +@Composable +@Preview(showBackground = true) +internal fun FolderListItemBadgeWithZeroCountsPreview() { + PreviewWithThemes { + FolderListItemBadge( + unreadCount = 0, + starredCount = 0, + showStarredCount = true, + ) + } +} + +@Composable +@Preview(showBackground = true) +internal fun FolderListItemBadgeWithoutStarredCountPreview() { + PreviewWithThemes { + FolderListItemBadge( + unreadCount = 99, + starredCount = 1, + showStarredCount = false, + ) + } +} + +@Composable +@Preview(showBackground = true) +internal fun FolderListItemBadgeWith100CountsPreview() { + PreviewWithThemes { + FolderListItemBadge( + unreadCount = 100, + starredCount = 100, + showStarredCount = true, + ) + } +} + +@Composable +@Preview(showBackground = true) +internal fun FolderListItemBadgeWith1000CountsPreview() { + PreviewWithThemes { + FolderListItemBadge( + unreadCount = 1000, + starredCount = 1000, + showStarredCount = true, + ) + } +} diff --git a/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/folder/FolderListItemPreview.kt b/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/folder/FolderListItemPreview.kt new file mode 100644 index 00000000000..18835b183b2 --- /dev/null +++ b/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/folder/FolderListItemPreview.kt @@ -0,0 +1,76 @@ +package app.k9mail.feature.navigation.drawer.ui.folder + +import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.Preview +import app.k9mail.core.mail.folder.api.FolderType +import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes +import app.k9mail.feature.navigation.drawer.ui.FakeData.DISPLAY_FOLDER + +@Composable +@Preview(showBackground = true) +fun FolderListItemPreview() { + PreviewWithThemes { + FolderListItem( + displayFolder = DISPLAY_FOLDER, + selected = false, + showStarredCount = false, + onClick = {}, + ) + } +} + +@Composable +@Preview(showBackground = true) +fun FolderListItemSelectedPreview() { + PreviewWithThemes { + FolderListItem( + displayFolder = DISPLAY_FOLDER, + selected = true, + showStarredCount = false, + onClick = {}, + ) + } +} + +@Composable +@Preview(showBackground = true) +fun FolderListItemWithStarredPreview() { + PreviewWithThemes { + FolderListItem( + displayFolder = DISPLAY_FOLDER, + selected = false, + showStarredCount = true, + onClick = {}, + ) + } +} + +@Composable +@Preview(showBackground = true) +fun FolderListItemWithStarredSelectedPreview() { + PreviewWithThemes { + FolderListItem( + displayFolder = DISPLAY_FOLDER, + selected = true, + showStarredCount = true, + onClick = {}, + ) + } +} + +@Composable +@Preview(showBackground = true) +fun FolderListItemWithInboxFolderPreview() { + PreviewWithThemes { + FolderListItem( + displayFolder = DISPLAY_FOLDER.copy( + folder = DISPLAY_FOLDER.folder.copy( + type = FolderType.INBOX, + ), + ), + selected = false, + showStarredCount = true, + onClick = {}, + ) + } +} diff --git a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/NavigationDrawerModule.kt b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/NavigationDrawerModule.kt index edb1f961952..835f3a893e2 100644 --- a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/NavigationDrawerModule.kt +++ b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/NavigationDrawerModule.kt @@ -2,6 +2,7 @@ package app.k9mail.feature.navigation.drawer import app.k9mail.feature.navigation.drawer.domain.DomainContract.UseCase import app.k9mail.feature.navigation.drawer.domain.usecase.GetDisplayAccounts +import app.k9mail.feature.navigation.drawer.domain.usecase.GetDisplayFoldersForAccount import app.k9mail.feature.navigation.drawer.legacy.AccountsViewModel import app.k9mail.feature.navigation.drawer.legacy.FoldersViewModel import app.k9mail.feature.navigation.drawer.ui.DrawerViewModel @@ -21,6 +22,12 @@ val navigationDrawerModule: Module = module { ) } + single { + GetDisplayFoldersForAccount( + repository = get(), + ) + } + viewModel { AccountsViewModel( getDisplayAccounts = get(), @@ -42,6 +49,7 @@ val navigationDrawerModule: Module = module { viewModel { DrawerViewModel( getDisplayAccounts = get(), + getDisplayFoldersForAccount = get(), ) } } diff --git a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/domain/DomainContract.kt b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/domain/DomainContract.kt index dec8f13f825..468de79c149 100644 --- a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/domain/DomainContract.kt +++ b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/domain/DomainContract.kt @@ -1,6 +1,7 @@ package app.k9mail.feature.navigation.drawer.domain import app.k9mail.feature.navigation.drawer.domain.entity.DisplayAccount +import app.k9mail.legacy.ui.folder.DisplayFolder import kotlinx.coroutines.flow.Flow interface DomainContract { @@ -9,5 +10,9 @@ interface DomainContract { fun interface GetDisplayAccounts { operator fun invoke(): Flow> } + + fun interface GetDisplayFoldersForAccount { + operator fun invoke(accountUuid: String): Flow> + } } } diff --git a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/domain/usecase/GetDisplayFoldersForAccount.kt b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/domain/usecase/GetDisplayFoldersForAccount.kt new file mode 100644 index 00000000000..a25c7b5033a --- /dev/null +++ b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/domain/usecase/GetDisplayFoldersForAccount.kt @@ -0,0 +1,14 @@ +package app.k9mail.feature.navigation.drawer.domain.usecase + +import app.k9mail.feature.navigation.drawer.domain.DomainContract.UseCase +import app.k9mail.legacy.ui.folder.DisplayFolder +import app.k9mail.legacy.ui.folder.DisplayFolderRepository +import kotlinx.coroutines.flow.Flow + +class GetDisplayFoldersForAccount( + private val repository: DisplayFolderRepository, +) : UseCase.GetDisplayFoldersForAccount { + override fun invoke(accountUuid: String): Flow> { + return repository.getDisplayFoldersFlow(accountUuid) + } +} diff --git a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerContent.kt b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerContent.kt index fd765cacfee..202bf9c7b08 100644 --- a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerContent.kt +++ b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerContent.kt @@ -4,20 +4,21 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag import app.k9mail.core.ui.compose.designsystem.atom.DividerHorizontal import app.k9mail.core.ui.compose.designsystem.atom.Surface -import app.k9mail.core.ui.compose.designsystem.organism.drawer.NavigationDrawerItem import app.k9mail.core.ui.compose.theme2.MainTheme +import app.k9mail.feature.navigation.drawer.ui.DrawerContract.Event import app.k9mail.feature.navigation.drawer.ui.DrawerContract.State import app.k9mail.feature.navigation.drawer.ui.account.AccountView +import app.k9mail.feature.navigation.drawer.ui.folder.FolderList @Composable fun DrawerContent( state: State, + onEvent: (Event) -> Unit, modifier: Modifier = Modifier, ) { Surface( @@ -38,36 +39,17 @@ fun DrawerContent( displayName = it.account.displayName, emailAddress = it.account.email, accountColor = it.account.chipColor, + onClick = { onEvent(Event.OnAccountViewClick(it)) }, ) DividerHorizontal() } - LazyColumn( - modifier = Modifier - .fillMaxSize(), - ) { - item { - NavigationDrawerItem( - label = "Folder1", - selected = true, - onClick = {}, - ) - } - item { - NavigationDrawerItem( - label = "Folder2", - selected = false, - onClick = {}, - ) - } - item { - NavigationDrawerItem( - label = "Folder3", - selected = false, - onClick = {}, - ) - } - } + FolderList( + folders = state.folders, + selectedFolder = state.folders.firstOrNull(), // TODO Use selected folder from state + onFolderClick = { }, + showStarredCount = state.showStarredCount, + ) } } } diff --git a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerContract.kt b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerContract.kt index 3f1cf11f9e8..d2652ddc2f6 100644 --- a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerContract.kt +++ b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerContract.kt @@ -1,20 +1,29 @@ package app.k9mail.feature.navigation.drawer.ui +import androidx.compose.runtime.Stable import app.k9mail.core.ui.compose.common.mvi.UnidirectionalViewModel import app.k9mail.feature.navigation.drawer.domain.entity.DisplayAccount +import app.k9mail.legacy.ui.folder.DisplayFolder +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf interface DrawerContract { interface ViewModel : UnidirectionalViewModel + @Stable data class State( val currentAccount: DisplayAccount? = null, - val accounts: List = emptyList(), + val accounts: ImmutableList = persistentListOf(), + val folders: ImmutableList = persistentListOf(), + val showStarredCount: Boolean = false, val isLoading: Boolean = false, ) sealed interface Event { data object OnRefresh : Event + data class OnAccountClick(val account: DisplayAccount) : Event + data class OnAccountViewClick(val account: DisplayAccount) : Event } sealed interface Effect diff --git a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerView.kt b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerView.kt index 01a4b5b6ce0..ca14902c944 100644 --- a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerView.kt +++ b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerView.kt @@ -3,20 +3,23 @@ package app.k9mail.feature.navigation.drawer.ui import androidx.compose.runtime.Composable import app.k9mail.core.ui.compose.common.mvi.observe import app.k9mail.core.ui.compose.designsystem.molecule.PullToRefreshBox +import app.k9mail.feature.navigation.drawer.ui.DrawerContract.Event +import app.k9mail.feature.navigation.drawer.ui.DrawerContract.ViewModel import org.koin.androidx.compose.koinViewModel @Composable fun DrawerView( - viewModel: DrawerContract.ViewModel = koinViewModel(), + viewModel: ViewModel = koinViewModel(), ) { val (state, dispatch) = viewModel.observe { } PullToRefreshBox( isRefreshing = state.value.isLoading, - onRefresh = { dispatch(DrawerContract.Event.OnRefresh) }, + onRefresh = { dispatch(Event.OnRefresh) }, ) { DrawerContent( state = state.value, + onEvent = { dispatch(it) }, ) } } diff --git a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerViewModel.kt b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerViewModel.kt index 65b43bb4740..87a7f664bc6 100644 --- a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerViewModel.kt +++ b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerViewModel.kt @@ -8,12 +8,20 @@ import app.k9mail.feature.navigation.drawer.ui.DrawerContract.Effect import app.k9mail.feature.navigation.drawer.ui.DrawerContract.Event import app.k9mail.feature.navigation.drawer.ui.DrawerContract.State import app.k9mail.feature.navigation.drawer.ui.DrawerContract.ViewModel +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toImmutableList +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.launch @Suppress("MagicNumber") class DrawerViewModel( private val getDisplayAccounts: UseCase.GetDisplayAccounts, + private val getDisplayFoldersForAccount: UseCase.GetDisplayFoldersForAccount, initialState: State = State(), ) : BaseViewModel( initialState = initialState, @@ -22,7 +30,17 @@ class DrawerViewModel( init { viewModelScope.launch { - getDisplayAccounts().collect { accounts -> updateAccounts(accounts) } + loadAccounts() + } + + viewModelScope.launch { + loadFolders() + } + } + + private suspend fun loadAccounts() { + getDisplayAccounts().collectLatest { accounts -> + updateAccounts(accounts) } } @@ -32,19 +50,59 @@ class DrawerViewModel( updateState { if (isCurrentAccountAvailable) { - it.copy(accounts = accounts) + it.copy(accounts = accounts.toImmutableList()) } else { it.copy( + accounts = accounts.toImmutableList(), currentAccount = accounts.firstOrNull(), - accounts = accounts, ) } } } + @OptIn(ExperimentalCoroutinesApi::class) + private suspend fun loadFolders() { + state.mapNotNull { it.currentAccount?.account?.uuid } + .distinctUntilChanged() + .flatMapLatest { accountUuid -> + getDisplayFoldersForAccount(accountUuid) + }.collectLatest { folders -> + updateState { + it.copy(folders = folders.toImmutableList()) + } + } + } + override fun event(event: Event) { when (event) { Event.OnRefresh -> refresh() + is Event.OnAccountClick -> selectAccount(event.account) + is Event.OnAccountViewClick -> { + selectAccount( + state.value.accounts.nextOrFirst(event.account)!!, + ) + } + } + } + + private fun selectAccount(account: DisplayAccount) { + viewModelScope.launch { + updateState { + it.copy( + currentAccount = account, + ) + } + } + } + + private fun ImmutableList.nextOrFirst(account: DisplayAccount): DisplayAccount? { + val index = indexOf(account) + return if (index == -1) { + null + } else if (index == size - 1) { + get(0) + } else { + get(index + 1) } } diff --git a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/account/AccountView.kt b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/account/AccountView.kt index d4d16d78894..4cdbeda9110 100644 --- a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/account/AccountView.kt +++ b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/account/AccountView.kt @@ -1,5 +1,6 @@ package app.k9mail.feature.navigation.drawer.ui.account +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.IntrinsicSize @@ -21,11 +22,13 @@ fun AccountView( emailAddress: String, accountColor: Int, modifier: Modifier = Modifier, + onClick: () -> Unit, ) { Row( modifier = modifier .fillMaxWidth() .height(intrinsicSize = IntrinsicSize.Max) + .clickable(onClick = onClick) .padding( top = MainTheme.spacings.default, start = MainTheme.spacings.double, diff --git a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/folder/FolderList.kt b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/folder/FolderList.kt new file mode 100644 index 00000000000..cfbe88d7c90 --- /dev/null +++ b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/folder/FolderList.kt @@ -0,0 +1,32 @@ +package app.k9mail.feature.navigation.drawer.ui.folder + +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import app.k9mail.legacy.ui.folder.DisplayFolder +import kotlinx.collections.immutable.ImmutableList + +@Composable +fun FolderList( + folders: ImmutableList, + selectedFolder: DisplayFolder?, + onFolderClick: (DisplayFolder) -> Unit, + showStarredCount: Boolean, + modifier: Modifier = Modifier, +) { + LazyColumn( + modifier = modifier + .fillMaxSize(), + ) { + items(folders) { folder -> + FolderListItem( + displayFolder = folder, + selected = folder == selectedFolder, + showStarredCount = showStarredCount, + onClick = onFolderClick, + ) + } + } +} diff --git a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/folder/FolderListItem.kt b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/folder/FolderListItem.kt new file mode 100644 index 00000000000..c9019e23791 --- /dev/null +++ b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/folder/FolderListItem.kt @@ -0,0 +1,51 @@ +package app.k9mail.feature.navigation.drawer.ui.folder + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import app.k9mail.core.mail.folder.api.FolderType +import app.k9mail.core.ui.compose.designsystem.atom.icon.Icon +import app.k9mail.core.ui.compose.designsystem.atom.icon.Icons +import app.k9mail.core.ui.compose.designsystem.organism.drawer.NavigationDrawerItem +import app.k9mail.legacy.ui.folder.DisplayFolder + +@Composable +fun FolderListItem( + displayFolder: DisplayFolder, + selected: Boolean, + showStarredCount: Boolean, + onClick: (DisplayFolder) -> Unit, + modifier: Modifier = Modifier, +) { + NavigationDrawerItem( + label = displayFolder.folder.name, + selected = selected, + modifier = modifier, + onClick = { onClick(displayFolder) }, + icon = { + Icon( + imageVector = mapFolderIcon(displayFolder.folder.type), + ) + }, + badge = { + FolderListItemBadge( + unreadCount = displayFolder.unreadMessageCount, + starredCount = displayFolder.starredMessageCount, + showStarredCount = showStarredCount, + ) + }, + ) +} + +private fun mapFolderIcon(type: FolderType): ImageVector { + return when (type) { + FolderType.INBOX -> Icons.Outlined.Inbox + FolderType.OUTBOX -> Icons.Outlined.Outbox + FolderType.SENT -> Icons.Outlined.Send + FolderType.TRASH -> Icons.Outlined.Delete + FolderType.DRAFTS -> Icons.Outlined.Drafts + FolderType.ARCHIVE -> Icons.Outlined.Archive + FolderType.SPAM -> Icons.Outlined.Report + FolderType.REGULAR -> Icons.Outlined.Folder + } +} diff --git a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/folder/FolderListItemBadge.kt b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/folder/FolderListItemBadge.kt new file mode 100644 index 00000000000..9fdbf8d2851 --- /dev/null +++ b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/folder/FolderListItemBadge.kt @@ -0,0 +1,107 @@ +package app.k9mail.feature.navigation.drawer.ui.folder + +import android.content.res.Resources +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import app.k9mail.core.ui.compose.designsystem.atom.icon.Icons +import app.k9mail.core.ui.compose.designsystem.organism.drawer.NavigationDrawerItemBadge +import app.k9mail.core.ui.compose.theme2.MainTheme +import app.k9mail.feature.navigation.drawer.R + +@Composable +fun FolderListItemBadge( + unreadCount: Int, + starredCount: Int, + showStarredCount: Boolean, + modifier: Modifier = Modifier, +) { + if (showStarredCount) { + FolderCountAndStarredBadge( + unreadCount = unreadCount, + starredCount = starredCount, + modifier = modifier, + ) + } else { + FolderCountBadge( + unreadCount = unreadCount, + modifier = modifier, + ) + } +} + +@Composable +private fun FolderCountBadge( + unreadCount: Int, + modifier: Modifier = Modifier, +) { + if (unreadCount > 0) { + val resources = LocalContext.current.resources + + NavigationDrawerItemBadge( + label = labelForCount( + count = unreadCount, + resources = resources, + ), + modifier = modifier, + ) + } +} + +@Composable +private fun FolderCountAndStarredBadge( + unreadCount: Int, + starredCount: Int, + modifier: Modifier = Modifier, +) { + if (unreadCount > 0 || starredCount > 0) { + Row( + modifier = modifier, + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(MainTheme.spacings.default), + ) { + val resources = LocalContext.current.resources + + if (unreadCount > 0) { + NavigationDrawerItemBadge( + label = labelForCount( + count = unreadCount, + resources = resources, + ), + imageVector = Icons.Filled.Dot, + ) + } + + if (starredCount > 0) { + NavigationDrawerItemBadge( + label = labelForCount( + count = starredCount, + resources = resources, + ), + imageVector = Icons.Filled.Star, + ) + } + } + } +} + +@Suppress("MagicNumber") +private fun labelForCount( + count: Int, + resources: Resources, +) = when { + count in 1..99 -> "$count" + + count in 100..1000 -> resources.getString( + R.string.navigation_drawer_folder_item_badge_count_greater_than_99, + ) + + count > 1000 -> resources.getString( + R.string.navigation_drawer_folder_item_badge_count_greater_than_1_000, + ) + + else -> "" +} diff --git a/feature/navigation/drawer/src/main/res/values/strings.xml b/feature/navigation/drawer/src/main/res/values/strings.xml index f71f5c69b51..05163e56402 100644 --- a/feature/navigation/drawer/src/main/res/values/strings.xml +++ b/feature/navigation/drawer/src/main/res/values/strings.xml @@ -3,4 +3,6 @@ Settings Manage folders Unified Inbox + 99+ + 1k+ diff --git a/feature/navigation/drawer/src/test/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerViewModelTest.kt b/feature/navigation/drawer/src/test/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerViewModelTest.kt index c1d0725bc4f..e4fdece880c 100644 --- a/feature/navigation/drawer/src/test/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerViewModelTest.kt +++ b/feature/navigation/drawer/src/test/kotlin/app/k9mail/feature/navigation/drawer/ui/DrawerViewModelTest.kt @@ -1,5 +1,7 @@ package app.k9mail.feature.navigation.drawer.ui +import app.k9mail.core.mail.folder.api.Folder +import app.k9mail.core.mail.folder.api.FolderType import app.k9mail.core.ui.compose.testing.MainDispatcherRule import app.k9mail.core.ui.compose.testing.mvi.eventStateTest import app.k9mail.feature.navigation.drawer.domain.entity.DisplayAccount @@ -7,6 +9,7 @@ import app.k9mail.feature.navigation.drawer.ui.DrawerContract.Event import app.k9mail.feature.navigation.drawer.ui.DrawerContract.State import app.k9mail.legacy.account.Account import app.k9mail.legacy.account.Identity +import app.k9mail.legacy.ui.folder.DisplayFolder import assertk.assertThat import assertk.assertions.isEqualTo import kotlin.test.Test @@ -46,7 +49,7 @@ class DrawerViewModelTest { val displayAccounts = createDisplayAccountList(3) val getDisplayAccountsFlow = MutableStateFlow(displayAccounts) val testSubject = createTestSubject( - getDisplayAccountsFlow = getDisplayAccountsFlow, + displayAccountsFlow = getDisplayAccountsFlow, ) advanceUntilIdle() @@ -61,7 +64,7 @@ class DrawerViewModelTest { val displayAccounts = createDisplayAccountList(3) val getDisplayAccountsFlow = MutableStateFlow(displayAccounts) val testSubject = createTestSubject( - getDisplayAccountsFlow = getDisplayAccountsFlow, + displayAccountsFlow = getDisplayAccountsFlow, ) advanceUntilIdle() @@ -80,7 +83,7 @@ class DrawerViewModelTest { fun `should set current account to null when no accounts are present`() = runTest { val getDisplayAccountsFlow = MutableStateFlow(emptyList()) val testSubject = createTestSubject( - getDisplayAccountsFlow = getDisplayAccountsFlow, + displayAccountsFlow = getDisplayAccountsFlow, ) advanceUntilIdle() @@ -89,11 +92,76 @@ class DrawerViewModelTest { assertThat(testSubject.state.value.currentAccount).isEqualTo(null) } + @Test + fun `should set current account when OnAccountClick event is received`() = runTest { + val displayAccounts = createDisplayAccountList(3) + val getDisplayAccountsFlow = MutableStateFlow(displayAccounts) + val testSubject = createTestSubject( + displayAccountsFlow = getDisplayAccountsFlow, + ) + + advanceUntilIdle() + + testSubject.event(Event.OnAccountClick(displayAccounts[1])) + + advanceUntilIdle() + + assertThat(testSubject.state.value.currentAccount).isEqualTo(displayAccounts[1]) + } + + @Test + fun `should collect display folders for current account`() = runTest { + val displayAccounts = createDisplayAccountList(3) + val getDisplayAccountsFlow = MutableStateFlow(displayAccounts) + val displayFoldersMap = mapOf( + displayAccounts[0].account.uuid to createDisplayFolderList(3), + ) + val testSubject = createTestSubject( + displayAccountsFlow = getDisplayAccountsFlow, + displayFoldersMap = displayFoldersMap, + ) + + advanceUntilIdle() + + val displayFolders = displayFoldersMap[displayAccounts[0].account.uuid] ?: emptyList() + assertThat(testSubject.state.value.folders.size).isEqualTo(displayFolders.size) + assertThat(testSubject.state.value.folders).isEqualTo(displayFolders) + } + + @Test + fun `should collect display folders when current account is changed`() = runTest { + val displayAccounts = createDisplayAccountList(3) + val getDisplayAccountsFlow = MutableStateFlow(displayAccounts) + val displayFoldersMap = mapOf( + displayAccounts[0].account.uuid to createDisplayFolderList(1), + displayAccounts[1].account.uuid to createDisplayFolderList(5), + displayAccounts[2].account.uuid to createDisplayFolderList(10), + ) + val testSubject = createTestSubject( + displayAccountsFlow = getDisplayAccountsFlow, + displayFoldersMap = displayFoldersMap, + ) + + advanceUntilIdle() + + testSubject.event(Event.OnAccountClick(displayAccounts[1])) + + advanceUntilIdle() + + val displayFolders = displayFoldersMap[displayAccounts[1].account.uuid] ?: emptyList() + assertThat(testSubject.state.value.folders.size).isEqualTo(displayFolders.size) + assertThat(testSubject.state.value.folders).isEqualTo(displayFolders) + } + private fun createTestSubject( - getDisplayAccountsFlow: Flow> = flow { emit(emptyList()) }, + displayAccountsFlow: Flow> = flow { emit(emptyList()) }, + displayFoldersMap: Map> = emptyMap(), ): DrawerViewModel { return DrawerViewModel( - getDisplayAccounts = { getDisplayAccountsFlow }, + getDisplayAccounts = { displayAccountsFlow }, + getDisplayFoldersForAccount = { accountUuid -> + flow { emit(displayFoldersMap[accountUuid] ?: emptyList()) } + }, ) } @@ -134,4 +202,34 @@ class DrawerViewModelTest { ) } } + + private fun createDisplayFolder( + id: Long = 1234, + name: String = "name", + type: FolderType = FolderType.REGULAR, + unreadCount: Int = 0, + starredCount: Int = 0, + ): DisplayFolder { + val folder = Folder( + id = id, + name = name, + type = type, + isLocalOnly = false, + ) + + return DisplayFolder( + folder = folder, + isInTopGroup = false, + unreadMessageCount = unreadCount, + starredMessageCount = starredCount, + ) + } + + private fun createDisplayFolderList(count: Int): List { + return List(count) { index -> + createDisplayFolder( + id = index.toLong() + 100, + ) + } + } } diff --git a/feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/ui/PermissionBox.kt b/feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/ui/PermissionBox.kt index f3fbed4b0fd..665a156ef2b 100644 --- a/feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/ui/PermissionBox.kt +++ b/feature/onboarding/permissions/src/main/kotlin/app/k9mail/feature/onboarding/permissions/ui/PermissionBox.kt @@ -85,7 +85,7 @@ private fun IconWithPermissionStateOverlay( permissionState: UiPermissionState, ) { Box { - val iconSize = MainTheme.sizes.largeIcon + val iconSize = MainTheme.sizes.iconLarge val overlayIconSize = iconSize / 2 val overlayIconOffset = overlayIconSize / 2 val scalingFactor = iconSize / icon.image.defaultHeight