From e4ba6a2562ea11a2de105e62d07d4b81a5c8e8db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolf-Martell=20Montw=C3=A9?= Date: Tue, 10 Sep 2024 19:07:57 +0200 Subject: [PATCH 1/4] Add AccountIndicator --- .../compose/theme2/SelectThemeColorScheme.kt | 2 +- .../ui/account/AccountIndicatorPreview.kt | 43 +++++++++++++++++++ .../drawer/ui/account/AccountIndicator.kt | 32 ++++++++++++++ 3 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/account/AccountIndicatorPreview.kt create mode 100644 feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/account/AccountIndicator.kt diff --git a/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/SelectThemeColorScheme.kt b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/SelectThemeColorScheme.kt index ee9b3519cb..d1f44da6d8 100644 --- a/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/SelectThemeColorScheme.kt +++ b/core/ui/compose/theme2/common/src/main/kotlin/app/k9mail/core/ui/compose/theme2/SelectThemeColorScheme.kt @@ -113,4 +113,4 @@ private fun ColorScheme.toDynamicThemeColorScheme( ) } -private fun Color.toHarmonizedColor(target: Color) = Color(MaterialColors.harmonize(toArgb(), target.toArgb())) +fun Color.toHarmonizedColor(target: Color) = Color(MaterialColors.harmonize(toArgb(), target.toArgb())) diff --git a/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/account/AccountIndicatorPreview.kt b/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/account/AccountIndicatorPreview.kt new file mode 100644 index 0000000000..dd9930c212 --- /dev/null +++ b/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/account/AccountIndicatorPreview.kt @@ -0,0 +1,43 @@ +package app.k9mail.feature.navigation.drawer.ui.account + +import androidx.compose.foundation.layout.height +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.tooling.preview.Preview +import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes +import app.k9mail.core.ui.compose.theme2.MainTheme + +@Composable +@Preview(showBackground = true) +internal fun AccountIndicatorPreview() { + PreviewWithThemes { + AccountIndicator( + accountColor = 0, + modifier = Modifier.height(MainTheme.spacings.double), + ) + } +} + +@Composable +@Preview(showBackground = true) +internal fun AccountIndicatorPreviewWithYellowAccountColor() { + PreviewWithThemes { + AccountIndicator( + accountColor = Color.Yellow.toArgb(), + modifier = Modifier.height(MainTheme.spacings.double), + ) + } +} + +@Composable +@Preview(showBackground = true) +internal fun AccountIndicatorPreviewWithGrayAccountColor() { + PreviewWithThemes { + AccountIndicator( + accountColor = Color.Gray.toArgb(), + modifier = Modifier.height(MainTheme.spacings.double), + ) + } +} diff --git a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/account/AccountIndicator.kt b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/account/AccountIndicator.kt new file mode 100644 index 0000000000..fadd773475 --- /dev/null +++ b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/account/AccountIndicator.kt @@ -0,0 +1,32 @@ +package app.k9mail.feature.navigation.drawer.ui.account + +import androidx.compose.foundation.layout.defaultMinSize +import androidx.compose.foundation.layout.width +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import app.k9mail.core.ui.compose.designsystem.atom.Surface +import app.k9mail.core.ui.compose.theme2.MainTheme +import app.k9mail.core.ui.compose.theme2.toHarmonizedColor + +@Composable +fun AccountIndicator( + accountColor: Int, + modifier: Modifier = Modifier, +) { + val color = if (accountColor == 0) { + MainTheme.colors.primary + } else { + Color(accountColor).toHarmonizedColor(MainTheme.colors.surface) + } + + Surface( + modifier = modifier + .width(MainTheme.spacings.half) + .defaultMinSize( + minHeight = MainTheme.spacings.default, + ), + color = color, + shape = MainTheme.shapes.medium, + ) {} +} From 913a7e057036d220a2dc86c6932a1a4b748f7d86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolf-Martell=20Montw=C3=A9?= Date: Wed, 11 Sep 2024 11:35:31 +0200 Subject: [PATCH 2/4] Add AccountView --- .../drawer/ui/account/AccountViewPreview.kt | 60 +++++++++++++++++++ .../drawer/ui/account/AccountView.kt | 58 ++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/account/AccountViewPreview.kt create mode 100644 feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/account/AccountView.kt 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 new file mode 100644 index 0000000000..b2797ffef4 --- /dev/null +++ b/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/account/AccountViewPreview.kt @@ -0,0 +1,60 @@ +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 + +@Composable +@Preview(showBackground = true) +internal fun AccountViewPreview() { + PreviewWithThemes { + AccountView( + displayName = DISPLAY_NAME, + emailAddress = EMAIL_ADDRESS, + accountColor = 0, + ) + } +} + +@Composable +@Preview(showBackground = true) +internal fun AccountViewWithColorPreview() { + PreviewWithThemes { + AccountView( + displayName = DISPLAY_NAME, + emailAddress = EMAIL_ADDRESS, + accountColor = 0xFF0000, + ) + } +} + +@Composable +@Preview(showBackground = true) +internal fun AccountViewWithLongDisplayName() { + PreviewWithThemes { + AccountView( + displayName = "$LONG_TEXT $DISPLAY_NAME", + emailAddress = EMAIL_ADDRESS, + accountColor = 0, + ) + } +} + +@Composable +@Preview(showBackground = true) +internal fun AccountViewWithLongEmailPreview() { + PreviewWithThemes { + AccountView( + displayName = DISPLAY_NAME, + emailAddress = "$LONG_TEXT@example.com", + accountColor = 0, + ) + } +} + +private const val DISPLAY_NAME = "Account Name" + +private const val EMAIL_ADDRESS = "test@example.com" + +private const val LONG_TEXT = "loremipsumdolorsitametconsetetursadipscingelitr" + + "seddiamnonumyeirmodtemporinviduntutlaboreetdoloremagnaaliquyameratseddiamvoluptua" 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 new file mode 100644 index 0000000000..d4d16d7889 --- /dev/null +++ b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/ui/account/AccountView.kt @@ -0,0 +1,58 @@ +package app.k9mail.feature.navigation.drawer.ui.account + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodyLarge +import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodyMedium +import app.k9mail.core.ui.compose.theme2.MainTheme + +@Composable +fun AccountView( + displayName: String, + emailAddress: String, + accountColor: Int, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier + .fillMaxWidth() + .height(intrinsicSize = IntrinsicSize.Max) + .padding( + top = MainTheme.spacings.default, + start = MainTheme.spacings.double, + end = MainTheme.spacings.triple, + bottom = MainTheme.spacings.oneHalf, + ), + verticalAlignment = Alignment.CenterVertically, + ) { + AccountIndicator( + accountColor = accountColor, + modifier = Modifier + .fillMaxHeight() + .padding( + end = MainTheme.spacings.default, + ), + ) + Column( + verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.half), + ) { + TextBodyLarge( + text = displayName, + color = MainTheme.colors.onSurface, + ) + TextBodyMedium( + text = emailAddress, + color = MainTheme.colors.onSurfaceVariant, + ) + } + } +} From 1dce31a5b7800613adaf923e7736434f4347846e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolf-Martell=20Montw=C3=A9?= Date: Wed, 11 Sep 2024 11:43:20 +0200 Subject: [PATCH 3/4] Change use case to operator function for easier use --- .../k9mail/feature/navigation/drawer/domain/DomainContract.kt | 2 +- .../navigation/drawer/domain/usecase/GetDisplayAccounts.kt | 2 +- .../feature/navigation/drawer/legacy/AccountsViewModel.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) 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 61d6fcc9bd..dec8f13f82 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 @@ -7,7 +7,7 @@ interface DomainContract { interface UseCase { fun interface GetDisplayAccounts { - fun execute(): Flow> + operator fun invoke(): Flow> } } } diff --git a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/domain/usecase/GetDisplayAccounts.kt b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/domain/usecase/GetDisplayAccounts.kt index 7327e90c47..17e1435a00 100644 --- a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/domain/usecase/GetDisplayAccounts.kt +++ b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/domain/usecase/GetDisplayAccounts.kt @@ -27,7 +27,7 @@ class GetDisplayAccounts( ) : UseCase.GetDisplayAccounts { @OptIn(ExperimentalCoroutinesApi::class) - override fun execute(): Flow> { + override fun invoke(): Flow> { return accountManager.getAccountsFlow() .flatMapLatest { accounts -> val messageCountsFlows: List> = accounts.map { account -> diff --git a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/legacy/AccountsViewModel.kt b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/legacy/AccountsViewModel.kt index ca630d6bab..351a9107d0 100644 --- a/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/legacy/AccountsViewModel.kt +++ b/feature/navigation/drawer/src/main/kotlin/app/k9mail/feature/navigation/drawer/legacy/AccountsViewModel.kt @@ -9,5 +9,5 @@ import app.k9mail.feature.navigation.drawer.domain.entity.DisplayAccount class AccountsViewModel( getDisplayAccounts: UseCase.GetDisplayAccounts, ) : ViewModel() { - val displayAccountsLiveData: LiveData> = getDisplayAccounts.execute().asLiveData() + val displayAccountsLiveData: LiveData> = getDisplayAccounts().asLiveData() } From 3e1c976a9c2e299951cdf59ac52480b07aaf7869 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolf-Martell=20Montw=C3=A9?= Date: Wed, 11 Sep 2024 11:47:43 +0200 Subject: [PATCH 4/4] Add account view to drawer content --- .../drawer/ui/DrawerContentPreview.kt | 21 +++- .../drawer/ui/account/AccountViewPreview.kt | 10 +- .../navigation/drawer/ui/account/FakeData.kt | 37 +++++++ .../drawer/NavigationDrawerModule.kt | 6 +- .../navigation/drawer/ui/DrawerContent.kt | 59 ++++++---- .../navigation/drawer/ui/DrawerContract.kt | 3 + .../navigation/drawer/ui/DrawerView.kt | 4 +- .../navigation/drawer/ui/DrawerViewModel.kt | 25 +++++ .../drawer/ui/DrawerViewModelTest.kt | 104 +++++++++++++++++- 9 files changed, 238 insertions(+), 31 deletions(-) create mode 100644 feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/account/FakeData.kt 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 b3c598e25d..0d37746131 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,11 +3,30 @@ 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 @Composable @Preview(showBackground = true) internal fun DrawerContentPreview() { PreviewWithTheme { - DrawerContent() + DrawerContent( + state = DrawerContract.State( + accounts = emptyList(), + currentAccount = null, + ), + ) + } +} + +@Composable +@Preview(showBackground = true) +fun DrawerContentWithAccountPreview() { + PreviewWithTheme { + DrawerContent( + state = DrawerContract.State( + accounts = listOf(DISPLAY_ACCOUNT), + currentAccount = DISPLAY_ACCOUNT, + ), + ) } } 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 b2797ffef4..b268f89489 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,6 +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 @Composable @Preview(showBackground = true) @@ -51,10 +54,3 @@ internal fun AccountViewWithLongEmailPreview() { ) } } - -private const val DISPLAY_NAME = "Account Name" - -private const val EMAIL_ADDRESS = "test@example.com" - -private const val LONG_TEXT = "loremipsumdolorsitametconsetetursadipscingelitr" + - "seddiamnonumyeirmodtemporinviduntutlaboreetdoloremagnaaliquyameratseddiamvoluptua" 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/account/FakeData.kt new file mode 100644 index 0000000000..3bcbd01748 --- /dev/null +++ b/feature/navigation/drawer/src/debug/kotlin/app/k9mail/feature/navigation/drawer/ui/account/FakeData.kt @@ -0,0 +1,37 @@ +package app.k9mail.feature.navigation.drawer.ui.account + +import app.k9mail.feature.navigation.drawer.domain.entity.DisplayAccount +import app.k9mail.legacy.account.Account +import app.k9mail.legacy.account.Identity + +internal object FakeData { + + const val ACCOUNT_UUID = "uuid" + const val DISPLAY_NAME = "Account Name" + const val EMAIL_ADDRESS = "test@example.com" + + const val LONG_TEXT = "loremipsumdolorsitametconsetetursadipscingelitr" + + "seddiamnonumyeirmodtemporinviduntutlaboreetdoloremagnaaliquyameratseddiamvoluptua" + + val ACCOUNT = Account( + uuid = ACCOUNT_UUID, + ).apply { + identities = ArrayList() + + val identity = Identity( + signatureUse = false, + signature = "", + description = "", + ) + identities.add(identity) + + name = DISPLAY_NAME + email = EMAIL_ADDRESS + } + + val DISPLAY_ACCOUNT = DisplayAccount( + account = ACCOUNT, + unreadMessageCount = 0, + starredMessageCount = 0, + ) +} 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 0c17eb6c32..edb1f96195 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 @@ -39,5 +39,9 @@ val navigationDrawerModule: Module = module { ) } - viewModel { DrawerViewModel() } + viewModel { + DrawerViewModel( + getDisplayAccounts = get(), + ) + } } 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 b031991131..fd765cacfe 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 @@ -1,17 +1,23 @@ package app.k9mail.feature.navigation.drawer.ui +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.State +import app.k9mail.feature.navigation.drawer.ui.account.AccountView @Composable fun DrawerContent( + state: State, modifier: Modifier = Modifier, ) { Surface( @@ -19,33 +25,48 @@ fun DrawerContent( .fillMaxSize() .testTag("DrawerContent"), ) { - LazyColumn( + Column( modifier = Modifier .fillMaxSize() .padding( vertical = MainTheme.spacings.oneHalf, ), + verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.default), ) { - item { - NavigationDrawerItem( - label = "Folder1", - selected = true, - onClick = {}, + state.currentAccount?.let { + AccountView( + displayName = it.account.displayName, + emailAddress = it.account.email, + accountColor = it.account.chipColor, ) + + DividerHorizontal() } - item { - NavigationDrawerItem( - label = "Folder2", - selected = false, - onClick = {}, - ) - } - item { - NavigationDrawerItem( - label = "Folder3", - selected = false, - onClick = {}, - ) + 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 = {}, + ) + } } } } 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 d06a981cd0..3f1cf11f9e 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,12 +1,15 @@ package app.k9mail.feature.navigation.drawer.ui import app.k9mail.core.ui.compose.common.mvi.UnidirectionalViewModel +import app.k9mail.feature.navigation.drawer.domain.entity.DisplayAccount interface DrawerContract { interface ViewModel : UnidirectionalViewModel data class State( + val currentAccount: DisplayAccount? = null, + val accounts: List = emptyList(), val isLoading: Boolean = false, ) 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 22411299d5..01a4b5b6ce 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 @@ -15,6 +15,8 @@ fun DrawerView( isRefreshing = state.value.isLoading, onRefresh = { dispatch(DrawerContract.Event.OnRefresh) }, ) { - DrawerContent() + DrawerContent( + state = state.value, + ) } } 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 945e20eac8..65b43bb474 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 @@ -2,6 +2,8 @@ package app.k9mail.feature.navigation.drawer.ui import androidx.lifecycle.viewModelScope import app.k9mail.core.ui.compose.common.mvi.BaseViewModel +import app.k9mail.feature.navigation.drawer.domain.DomainContract.UseCase +import app.k9mail.feature.navigation.drawer.domain.entity.DisplayAccount 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 @@ -11,12 +13,35 @@ import kotlinx.coroutines.launch @Suppress("MagicNumber") class DrawerViewModel( + private val getDisplayAccounts: UseCase.GetDisplayAccounts, initialState: State = State(), ) : BaseViewModel( initialState = initialState, ), ViewModel { + init { + viewModelScope.launch { + getDisplayAccounts().collect { accounts -> updateAccounts(accounts) } + } + } + + private fun updateAccounts(accounts: List) { + val currentAccountUuid = state.value.currentAccount?.account?.uuid + val isCurrentAccountAvailable = accounts.any { currentAccountUuid == it.account.uuid } + + updateState { + if (isCurrentAccountAvailable) { + it.copy(accounts = accounts) + } else { + it.copy( + currentAccount = accounts.firstOrNull(), + accounts = accounts, + ) + } + } + } + override fun event(event: Event) { when (event) { Event.OnRefresh -> refresh() 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 a0f39c6bfa..c1d0725bc4 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 @@ -2,12 +2,18 @@ package app.k9mail.feature.navigation.drawer.ui 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 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 assertk.assertThat import assertk.assertions.isEqualTo import kotlin.test.Test import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flow import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import org.junit.Rule @@ -18,10 +24,10 @@ class DrawerViewModelTest { @get:Rule val mainDispatcherRule = MainDispatcherRule() - private val testSubject = DrawerViewModel() - @Test fun `should change loading state when OnRefresh event is received`() = runTest { + val testSubject = createTestSubject() + eventStateTest( viewModel = testSubject, initialState = State(isLoading = false), @@ -34,4 +40,98 @@ class DrawerViewModelTest { assertThat(testSubject.state.value.isLoading).isEqualTo(false) } + + @Test + fun `should collect display accounts when created and select first as current`() = runTest { + val displayAccounts = createDisplayAccountList(3) + val getDisplayAccountsFlow = MutableStateFlow(displayAccounts) + val testSubject = createTestSubject( + getDisplayAccountsFlow = getDisplayAccountsFlow, + ) + + advanceUntilIdle() + + assertThat(testSubject.state.value.accounts.size).isEqualTo(displayAccounts.size) + assertThat(testSubject.state.value.accounts).isEqualTo(displayAccounts) + assertThat(testSubject.state.value.currentAccount).isEqualTo(displayAccounts.first()) + } + + @Test + fun `should reselect current account when old not present anymore`() = runTest { + val displayAccounts = createDisplayAccountList(3) + val getDisplayAccountsFlow = MutableStateFlow(displayAccounts) + val testSubject = createTestSubject( + getDisplayAccountsFlow = getDisplayAccountsFlow, + ) + + advanceUntilIdle() + + val newDisplayAccounts = displayAccounts.drop(1) + getDisplayAccountsFlow.emit(newDisplayAccounts) + + advanceUntilIdle() + + assertThat(testSubject.state.value.accounts.size).isEqualTo(newDisplayAccounts.size) + assertThat(testSubject.state.value.accounts).isEqualTo(newDisplayAccounts) + assertThat(testSubject.state.value.currentAccount).isEqualTo(newDisplayAccounts.first()) + } + + @Test + fun `should set current account to null when no accounts are present`() = runTest { + val getDisplayAccountsFlow = MutableStateFlow(emptyList()) + val testSubject = createTestSubject( + getDisplayAccountsFlow = getDisplayAccountsFlow, + ) + + advanceUntilIdle() + + assertThat(testSubject.state.value.accounts.size).isEqualTo(0) + assertThat(testSubject.state.value.currentAccount).isEqualTo(null) + } + + private fun createTestSubject( + getDisplayAccountsFlow: Flow> = flow { emit(emptyList()) }, + ): DrawerViewModel { + return DrawerViewModel( + getDisplayAccounts = { getDisplayAccountsFlow }, + ) + } + + private fun createDisplayAccount( + uuid: String = "uuid", + name: String = "name", + email: String = "test@example.com", + unreadCount: Int = 0, + starredCount: Int = 0, + ): DisplayAccount { + val account = Account( + uuid = uuid, + ).also { + it.identities = ArrayList() + + val identity = Identity( + signatureUse = false, + signature = "", + description = "", + ) + it.identities.add(identity) + + it.name = name + it.email = email + } + + return DisplayAccount( + account = account, + unreadMessageCount = unreadCount, + starredMessageCount = starredCount, + ) + } + + private fun createDisplayAccountList(count: Int): List { + return List(count) { index -> + createDisplayAccount( + uuid = "uuid-$index", + ) + } + } }