0
0
mirror of https://github.com/thunderbird/thunderbird-android.git synced 2024-09-19 19:52:14 +02:00

Add OnAccountClick event to trigger account selection and add OnAccountViewClick on the account view for testing

This commit is contained in:
Wolf-Martell Montwé 2024-09-13 12:04:30 +02:00
parent 8d34ada101
commit 8afe58c644
No known key found for this signature in database
GPG Key ID: 6D45B21512ACBF72
8 changed files with 156 additions and 19 deletions

View File

@ -12,10 +12,11 @@ internal fun DrawerContentPreview() {
PreviewWithTheme {
DrawerContent(
state = DrawerContract.State(
currentAccount = null,
accounts = persistentListOf(),
currentAccount = null,
folders = persistentListOf(),
),
onEvent = {},
)
}
}
@ -29,6 +30,7 @@ fun DrawerContentWithAccountPreview() {
accounts = persistentListOf(DISPLAY_ACCOUNT),
currentAccount = DISPLAY_ACCOUNT,
),
onEvent = {},
)
}
}

View File

@ -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 = {},
)
}
}

View File

@ -10,6 +10,7 @@ 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.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
@ -17,6 +18,7 @@ import app.k9mail.feature.navigation.drawer.ui.folder.FolderList
@Composable
fun DrawerContent(
state: State,
onEvent: (Event) -> Unit,
modifier: Modifier = Modifier,
) {
Surface(
@ -37,6 +39,7 @@ fun DrawerContent(
displayName = it.account.displayName,
emailAddress = it.account.email,
accountColor = it.account.chipColor,
onClick = { onEvent(Event.OnAccountViewClick(it)) },
)
DividerHorizontal()

View File

@ -5,7 +5,6 @@ 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.immutableListOf
import kotlinx.collections.immutable.persistentListOf
interface DrawerContract {
@ -23,6 +22,8 @@ interface DrawerContract {
sealed interface Event {
data object OnRefresh : Event
data class OnAccountClick(val account: DisplayAccount) : Event
data class OnAccountViewClick(val account: DisplayAccount) : Event
}
sealed interface Effect

View File

@ -3,9 +3,9 @@ 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 org.koin.androidx.compose.koinViewModel
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(
@ -19,6 +19,7 @@ fun DrawerView(
) {
DrawerContent(
state = state.value,
onEvent = { dispatch(it) },
)
}
}

View File

@ -10,10 +10,12 @@ 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.map
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.launch
@Suppress("MagicNumber")
@ -58,23 +60,49 @@ class DrawerViewModel(
}
}
@OptIn(ExperimentalCoroutinesApi::class)
private suspend fun loadFolders() {
state.map { it.currentAccount }
state.mapNotNull { it.currentAccount?.account?.uuid }
.distinctUntilChanged()
.collectLatest { currentAccount ->
if (currentAccount != null) {
getDisplayFoldersForAccount(currentAccount.account.uuid).collectLatest { folders ->
.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<DisplayAccount>.nextOrFirst(account: DisplayAccount): DisplayAccount? {
val index = indexOf(account)
return if (index == -1) {
null
} else if (index == size - 1) {
get(0)
} else {
get(index + 1)
}
}

View File

@ -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,

View File

@ -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
@ -47,7 +49,7 @@ class DrawerViewModelTest {
val displayAccounts = createDisplayAccountList(3)
val getDisplayAccountsFlow = MutableStateFlow(displayAccounts)
val testSubject = createTestSubject(
getDisplayAccountsFlow = getDisplayAccountsFlow,
displayAccountsFlow = getDisplayAccountsFlow,
)
advanceUntilIdle()
@ -62,7 +64,7 @@ class DrawerViewModelTest {
val displayAccounts = createDisplayAccountList(3)
val getDisplayAccountsFlow = MutableStateFlow(displayAccounts)
val testSubject = createTestSubject(
getDisplayAccountsFlow = getDisplayAccountsFlow,
displayAccountsFlow = getDisplayAccountsFlow,
)
advanceUntilIdle()
@ -81,7 +83,7 @@ class DrawerViewModelTest {
fun `should set current account to null when no accounts are present`() = runTest {
val getDisplayAccountsFlow = MutableStateFlow(emptyList<DisplayAccount>())
val testSubject = createTestSubject(
getDisplayAccountsFlow = getDisplayAccountsFlow,
displayAccountsFlow = getDisplayAccountsFlow,
)
advanceUntilIdle()
@ -90,13 +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<List<DisplayAccount>> = flow { emit(emptyList()) },
getDisplayFoldersForAccount: Flow<List<DisplayFolder>> = flow { emit(emptyList()) },
displayAccountsFlow: Flow<List<DisplayAccount>> = flow { emit(emptyList()) },
displayFoldersMap: Map<String, List<DisplayFolder>> = emptyMap(),
): DrawerViewModel {
return DrawerViewModel(
getDisplayAccounts = { getDisplayAccountsFlow },
getDisplayFoldersForAccount = { getDisplayFoldersForAccount },
getDisplayAccounts = { displayAccountsFlow },
getDisplayFoldersForAccount = { accountUuid ->
flow { emit(displayFoldersMap[accountUuid] ?: emptyList()) }
},
)
}
@ -137,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<DisplayFolder> {
return List(count) { index ->
createDisplayFolder(
id = index.toLong() + 100,
)
}
}
}