Skip to content

Commit

Permalink
Add OnAccountClick event to trigger account selection and add OnAccou…
Browse files Browse the repository at this point in the history
…ntViewClick on the account view for testing
  • Loading branch information
wmontwe committed Sep 13, 2024
1 parent 8d34ada commit 8afe58c
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ internal fun DrawerContentPreview() {
PreviewWithTheme {
DrawerContent(
state = DrawerContract.State(
currentAccount = null,
accounts = persistentListOf(),
currentAccount = null,
folders = persistentListOf(),
),
onEvent = {},
)
}
}
Expand All @@ -29,6 +30,7 @@ fun DrawerContentWithAccountPreview() {
accounts = persistentListOf(DISPLAY_ACCOUNT),
currentAccount = DISPLAY_ACCOUNT,
),
onEvent = {},
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ internal fun AccountViewPreview() {
displayName = DISPLAY_NAME,
emailAddress = EMAIL_ADDRESS,
accountColor = 0,
onClick = {},
)
}
}
Expand All @@ -27,6 +28,7 @@ internal fun AccountViewWithColorPreview() {
displayName = DISPLAY_NAME,
emailAddress = EMAIL_ADDRESS,
accountColor = 0xFF0000,
onClick = {},
)
}
}
Expand All @@ -39,6 +41,7 @@ internal fun AccountViewWithLongDisplayName() {
displayName = "$LONG_TEXT $DISPLAY_NAME",
emailAddress = EMAIL_ADDRESS,
accountColor = 0,
onClick = {},
)
}
}
Expand All @@ -51,6 +54,7 @@ internal fun AccountViewWithLongEmailPreview() {
displayName = DISPLAY_NAME,
emailAddress = "$LONG_TEXT@example.com",
accountColor = 0,
onClick = {},
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ 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

@Composable
fun DrawerContent(
state: State,
onEvent: (Event) -> Unit,
modifier: Modifier = Modifier,
) {
Surface(
Expand All @@ -37,6 +39,7 @@ fun DrawerContent(
displayName = it.account.displayName,
emailAddress = it.account.email,
accountColor = it.account.chipColor,
onClick = { onEvent(Event.OnAccountViewClick(it)) },
)

DividerHorizontal()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -19,6 +19,7 @@ fun DrawerView(
) {
DrawerContent(
state = state.value,
onEvent = { dispatch(it) },
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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 ->
updateState {
it.copy(folders = folders.toImmutableList())
}
}
.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)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -47,7 +49,7 @@ class DrawerViewModelTest {
val displayAccounts = createDisplayAccountList(3)
val getDisplayAccountsFlow = MutableStateFlow(displayAccounts)
val testSubject = createTestSubject(
getDisplayAccountsFlow = getDisplayAccountsFlow,
displayAccountsFlow = getDisplayAccountsFlow,
)

advanceUntilIdle()
Expand All @@ -62,7 +64,7 @@ class DrawerViewModelTest {
val displayAccounts = createDisplayAccountList(3)
val getDisplayAccountsFlow = MutableStateFlow(displayAccounts)
val testSubject = createTestSubject(
getDisplayAccountsFlow = getDisplayAccountsFlow,
displayAccountsFlow = getDisplayAccountsFlow,
)

advanceUntilIdle()
Expand All @@ -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()
Expand All @@ -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()) }
},
)
}

Expand Down Expand Up @@ -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,
)
}
}
}

0 comments on commit 8afe58c

Please sign in to comment.