mirror of
https://github.com/thunderbird/thunderbird-android.git
synced 2024-09-19 19:52:14 +02:00
Add OnFolderClick event and add OpenFolder and CloseDrawer effect
This commit is contained in:
parent
008a5e281c
commit
937bb3da22
@ -29,6 +29,7 @@ fun DrawerContentWithAccountPreview() {
|
||||
state = DrawerContract.State(
|
||||
accounts = persistentListOf(DISPLAY_ACCOUNT),
|
||||
currentAccount = DISPLAY_ACCOUNT,
|
||||
folders = persistentListOf(),
|
||||
),
|
||||
onEvent = {},
|
||||
)
|
||||
|
@ -15,6 +15,8 @@ import org.koin.core.component.inject
|
||||
|
||||
class FolderDrawer(
|
||||
override val parent: AppCompatActivity,
|
||||
private val openFolder: (folderId: Long) -> Unit,
|
||||
createDrawerListener: () -> DrawerLayout.DrawerListener,
|
||||
) : NavigationDrawer, KoinComponent {
|
||||
|
||||
private val themeProvider: FeatureThemeProvider by inject()
|
||||
@ -28,10 +30,14 @@ class FolderDrawer(
|
||||
sliderView.visibility = View.GONE
|
||||
drawerView.visibility = View.VISIBLE
|
||||
swipeRefreshLayout.isEnabled = false
|
||||
drawer.addDrawerListener(createDrawerListener())
|
||||
|
||||
drawerView.setContent {
|
||||
themeProvider.WithTheme {
|
||||
DrawerView()
|
||||
DrawerView(
|
||||
openFolder = openFolder,
|
||||
closeDrawer = { close() },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -46,8 +46,10 @@ fun DrawerContent(
|
||||
}
|
||||
FolderList(
|
||||
folders = state.folders,
|
||||
selectedFolder = state.folders.firstOrNull(), // TODO Use selected folder from state
|
||||
onFolderClick = { },
|
||||
selectedFolder = state.selectedFolder,
|
||||
onFolderClick = { folder ->
|
||||
onEvent(Event.OnFolderClick(folder))
|
||||
},
|
||||
showStarredCount = state.showStarredCount,
|
||||
)
|
||||
}
|
||||
|
@ -13,18 +13,23 @@ interface DrawerContract {
|
||||
|
||||
@Stable
|
||||
data class State(
|
||||
val currentAccount: DisplayAccount? = null,
|
||||
val accounts: ImmutableList<DisplayAccount> = persistentListOf(),
|
||||
val currentAccount: DisplayAccount? = null,
|
||||
val folders: ImmutableList<DisplayFolder> = persistentListOf(),
|
||||
val selectedFolder: DisplayFolder? = null,
|
||||
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
|
||||
data class OnFolderClick(val folder: DisplayFolder) : Event
|
||||
data object OnRefresh : Event
|
||||
}
|
||||
|
||||
sealed interface Effect
|
||||
sealed interface Effect {
|
||||
data class OpenFolder(val folderId: Long) : Effect
|
||||
data object CloseDrawer : Effect
|
||||
}
|
||||
}
|
||||
|
@ -3,15 +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.Effect
|
||||
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(
|
||||
openFolder: (folderId: Long) -> Unit,
|
||||
closeDrawer: () -> Unit,
|
||||
viewModel: ViewModel = koinViewModel<DrawerViewModel>(),
|
||||
) {
|
||||
val (state, dispatch) = viewModel.observe { }
|
||||
val (state, dispatch) = viewModel.observe { effect ->
|
||||
when (effect) {
|
||||
is Effect.OpenFolder -> openFolder(effect.folderId)
|
||||
Effect.CloseDrawer -> closeDrawer()
|
||||
}
|
||||
}
|
||||
|
||||
PullToRefreshBox(
|
||||
isRefreshing = state.value.isLoading,
|
||||
|
@ -8,6 +8,7 @@ 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 app.k9mail.legacy.ui.folder.DisplayFolder
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
@ -77,6 +78,7 @@ class DrawerViewModel(
|
||||
when (event) {
|
||||
Event.OnRefresh -> refresh()
|
||||
is Event.OnAccountClick -> selectAccount(event.account)
|
||||
is Event.OnFolderClick -> selectFolder(event.folder)
|
||||
is Event.OnAccountViewClick -> {
|
||||
selectAccount(
|
||||
state.value.accounts.nextOrFirst(event.account)!!,
|
||||
@ -106,10 +108,19 @@ class DrawerViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
private fun refresh() {
|
||||
if (state.value.isLoading) {
|
||||
return
|
||||
private fun selectFolder(folder: DisplayFolder) {
|
||||
updateState {
|
||||
it.copy(selectedFolder = folder)
|
||||
}
|
||||
emitEffect(Effect.OpenFolder(folder.folder.id))
|
||||
|
||||
viewModelScope.launch {
|
||||
delay(DRAWER_CLOSE_DELAY)
|
||||
emitEffect(Effect.CloseDrawer)
|
||||
}
|
||||
}
|
||||
|
||||
private fun refresh() {
|
||||
viewModelScope.launch {
|
||||
updateState {
|
||||
it.copy(isLoading = true)
|
||||
@ -124,3 +135,9 @@ class DrawerViewModel(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delay before closing the drawer to avoid the drawer being closed immediately and give time
|
||||
* for the ripple effect to finish.
|
||||
*/
|
||||
private const val DRAWER_CLOSE_DELAY = 250L
|
||||
|
@ -0,0 +1,26 @@
|
||||
package app.k9mail.feature.navigation.drawer.ui
|
||||
|
||||
import app.k9mail.feature.navigation.drawer.ui.DrawerContract.State
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import org.junit.Test
|
||||
|
||||
class DrawerStateTest {
|
||||
|
||||
@Test
|
||||
fun `should set default values`() {
|
||||
val state = State()
|
||||
|
||||
assertThat(state).isEqualTo(
|
||||
State(
|
||||
accounts = persistentListOf(),
|
||||
currentAccount = null,
|
||||
folders = persistentListOf(),
|
||||
selectedFolder = null,
|
||||
showStarredCount = false,
|
||||
isLoading = false,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
@ -5,12 +5,44 @@ import androidx.compose.ui.test.printToString
|
||||
import app.k9mail.core.ui.compose.testing.ComposeTest
|
||||
import app.k9mail.core.ui.compose.testing.onNodeWithTag
|
||||
import app.k9mail.core.ui.compose.testing.setContentWithTheme
|
||||
import app.k9mail.feature.navigation.drawer.ui.DrawerContract.Effect
|
||||
import app.k9mail.feature.navigation.drawer.ui.DrawerContract.State
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import kotlin.test.Test
|
||||
import kotlinx.coroutines.test.runTest
|
||||
|
||||
class DrawerViewKtTest : ComposeTest() {
|
||||
|
||||
@Test
|
||||
fun `should delegate effects`() = runTest {
|
||||
val initialState = State()
|
||||
val viewModel = FakeDrawerViewModel(initialState)
|
||||
var openFolderCounter = 0
|
||||
var closeDrawerCounter = 0
|
||||
|
||||
setContentWithTheme {
|
||||
DrawerView(
|
||||
openFolder = { openFolderCounter++ },
|
||||
closeDrawer = { closeDrawerCounter++ },
|
||||
viewModel = viewModel,
|
||||
)
|
||||
}
|
||||
|
||||
assertThat(openFolderCounter).isEqualTo(0)
|
||||
assertThat(closeDrawerCounter).isEqualTo(0)
|
||||
|
||||
viewModel.effect(Effect.OpenFolder(1))
|
||||
|
||||
assertThat(openFolderCounter).isEqualTo(1)
|
||||
assertThat(closeDrawerCounter).isEqualTo(0)
|
||||
|
||||
viewModel.effect(Effect.CloseDrawer)
|
||||
|
||||
assertThat(openFolderCounter).isEqualTo(1)
|
||||
assertThat(closeDrawerCounter).isEqualTo(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `pull refresh should listen to view model state`() = runTest {
|
||||
val initialState = State(
|
||||
@ -20,6 +52,8 @@ class DrawerViewKtTest : ComposeTest() {
|
||||
|
||||
setContentWithTheme {
|
||||
DrawerView(
|
||||
openFolder = {},
|
||||
closeDrawer = {},
|
||||
viewModel = viewModel,
|
||||
)
|
||||
}
|
||||
|
@ -3,8 +3,12 @@ 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.assertThatAndEffectTurbineConsumed
|
||||
import app.k9mail.core.ui.compose.testing.mvi.eventStateTest
|
||||
import app.k9mail.core.ui.compose.testing.mvi.turbines
|
||||
import app.k9mail.core.ui.compose.testing.mvi.turbinesWithInitialStateCheck
|
||||
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
|
||||
import app.k9mail.legacy.account.Account
|
||||
@ -13,6 +17,8 @@ import app.k9mail.legacy.ui.folder.DisplayFolder
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import kotlin.test.Test
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@ -153,6 +159,38 @@ class DrawerViewModelTest {
|
||||
assertThat(testSubject.state.value.folders).isEqualTo(displayFolders)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should set selected folder when OnFolderClick event is received`() = runTest {
|
||||
val displayAccounts = createDisplayAccountList(3)
|
||||
val getDisplayAccountsFlow = MutableStateFlow(displayAccounts)
|
||||
val displayFoldersMap = mapOf(
|
||||
displayAccounts[0].account.uuid to createDisplayFolderList(3),
|
||||
)
|
||||
val initialState = State(
|
||||
accounts = displayAccounts.toImmutableList(),
|
||||
currentAccount = displayAccounts[0],
|
||||
folders = displayFoldersMap[displayAccounts[0].account.uuid]?.toImmutableList() ?: persistentListOf(),
|
||||
)
|
||||
val testSubject = createTestSubject(
|
||||
displayAccountsFlow = getDisplayAccountsFlow,
|
||||
displayFoldersMap = displayFoldersMap,
|
||||
)
|
||||
val turbines = turbinesWithInitialStateCheck(testSubject, initialState)
|
||||
|
||||
advanceUntilIdle()
|
||||
|
||||
val displayFolders = displayFoldersMap[displayAccounts[0].account.uuid] ?: emptyList()
|
||||
testSubject.event(Event.OnFolderClick(displayFolders[1]))
|
||||
|
||||
assertThat(turbines.awaitStateItem().selectedFolder).isEqualTo(displayFolders[1])
|
||||
|
||||
assertThat(turbines.awaitEffectItem()).isEqualTo(Effect.OpenFolder(displayFolders[1].folder.id))
|
||||
|
||||
turbines.assertThatAndEffectTurbineConsumed {
|
||||
isEqualTo(Effect.CloseDrawer)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createTestSubject(
|
||||
displayAccountsFlow: Flow<List<DisplayAccount>> = flow { emit(emptyList()) },
|
||||
displayFoldersMap: Map<String, List<DisplayFolder>> = emptyMap(),
|
||||
|
@ -608,6 +608,8 @@ open class MessageList :
|
||||
private fun initializeFolderDrawer() {
|
||||
navigationDrawer = FolderDrawer(
|
||||
parent = this,
|
||||
openFolder = { folderId -> openFolder(folderId) },
|
||||
createDrawerListener = { createDrawerListener() },
|
||||
)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user