mirror of
https://github.com/thunderbird/thunderbird-android.git
synced 2024-09-19 19:52:14 +02:00
Merge pull request #8128 from wmontwe/add-drawer-folder-list-part1
Add drawer folder list - Part 1
This commit is contained in:
commit
3cd270818a
@ -0,0 +1,6 @@
|
||||
package app.k9mail.ui.catalog.ui
|
||||
|
||||
interface CatalogPage {
|
||||
val displayName: String
|
||||
val isFullScreen: Boolean
|
||||
}
|
@ -29,15 +29,17 @@ fun CatalogAtomContent(
|
||||
pages = pages,
|
||||
initialPage = initialPage,
|
||||
modifier = modifier,
|
||||
) {
|
||||
when (it) {
|
||||
TYPOGRAPHY -> typographyItems()
|
||||
COLOR -> colorItems()
|
||||
BUTTON -> buttonItems()
|
||||
SELECTION_CONTROL -> selectionControlItems()
|
||||
TEXT_FIELD -> textFieldItems()
|
||||
ICON -> iconItems()
|
||||
IMAGE -> imageItems()
|
||||
}
|
||||
}
|
||||
onRenderPage = {
|
||||
when (it) {
|
||||
TYPOGRAPHY -> typographyItems()
|
||||
COLOR -> colorItems()
|
||||
BUTTON -> buttonItems()
|
||||
SELECTION_CONTROL -> selectionControlItems()
|
||||
TEXT_FIELD -> textFieldItems()
|
||||
ICON -> iconItems()
|
||||
IMAGE -> imageItems()
|
||||
}
|
||||
},
|
||||
onRenderFullScreenPage = {},
|
||||
)
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
package app.k9mail.ui.catalog.ui.atom
|
||||
|
||||
import app.k9mail.ui.catalog.ui.CatalogPage
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
enum class CatalogAtomPage(
|
||||
private val displayName: String,
|
||||
) {
|
||||
override val displayName: String,
|
||||
override val isFullScreen: Boolean = false,
|
||||
) : CatalogPage {
|
||||
TYPOGRAPHY("Typography"),
|
||||
COLOR("Colors"),
|
||||
BUTTON("Buttons"),
|
||||
|
@ -1,6 +1,5 @@
|
||||
package app.k9mail.ui.catalog.ui.common
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
@ -20,16 +19,17 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import app.k9mail.core.ui.compose.designsystem.template.ResponsiveContentWithSurface
|
||||
import app.k9mail.core.ui.compose.theme2.MainTheme
|
||||
import app.k9mail.ui.catalog.ui.CatalogPage
|
||||
import app.k9mail.ui.catalog.ui.common.list.fullSpanItem
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun <T> PagedContent(
|
||||
fun <T : CatalogPage> PagedContent(
|
||||
pages: ImmutableList<T>,
|
||||
initialPage: T,
|
||||
modifier: Modifier = Modifier,
|
||||
onRenderFullScreenPage: @Composable (T) -> Unit = {},
|
||||
onRenderPage: LazyGridScope.(T) -> Unit,
|
||||
) {
|
||||
val pagerState = rememberPagerState(
|
||||
@ -63,17 +63,21 @@ fun <T> PagedContent(
|
||||
state = pagerState,
|
||||
modifier = Modifier
|
||||
.fillMaxSize(),
|
||||
) { page ->
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Adaptive(MainTheme.sizes.larger),
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.imePadding(),
|
||||
horizontalArrangement = Arrangement.spacedBy(MainTheme.spacings.double),
|
||||
verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.double),
|
||||
) {
|
||||
onRenderPage(pages[page])
|
||||
fullSpanItem { Spacer(modifier = Modifier.height(MainTheme.sizes.smaller)) }
|
||||
) { pageIndex ->
|
||||
if (pages[pageIndex].isFullScreen) {
|
||||
onRenderFullScreenPage(pages[pageIndex])
|
||||
} else {
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Adaptive(MainTheme.sizes.larger),
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.imePadding(),
|
||||
horizontalArrangement = Arrangement.spacedBy(MainTheme.spacings.double),
|
||||
verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.double),
|
||||
) {
|
||||
onRenderPage(pages[pageIndex])
|
||||
fullSpanItem { Spacer(modifier = Modifier.height(MainTheme.sizes.smaller)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,9 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import app.k9mail.ui.catalog.ui.common.PagedContent
|
||||
import app.k9mail.ui.catalog.ui.molecule.CatalogMoleculePage.INPUT
|
||||
import app.k9mail.ui.catalog.ui.molecule.CatalogMoleculePage.PULL_TO_REFRESH
|
||||
import app.k9mail.ui.catalog.ui.molecule.CatalogMoleculePage.STATE
|
||||
import app.k9mail.ui.catalog.ui.molecule.items.PullToRefresh
|
||||
import app.k9mail.ui.catalog.ui.molecule.items.inputItems
|
||||
import app.k9mail.ui.catalog.ui.molecule.items.stateItems
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
@ -19,10 +21,18 @@ fun CatalogMoleculeContent(
|
||||
pages = pages,
|
||||
initialPage = initialPage,
|
||||
modifier = modifier,
|
||||
) {
|
||||
when (it) {
|
||||
INPUT -> inputItems()
|
||||
STATE -> stateItems()
|
||||
}
|
||||
}
|
||||
onRenderPage = {
|
||||
when (it) {
|
||||
INPUT -> inputItems()
|
||||
STATE -> stateItems()
|
||||
else -> throw IllegalArgumentException("Unknown page: $it")
|
||||
}
|
||||
},
|
||||
onRenderFullScreenPage = { page ->
|
||||
when (page) {
|
||||
PULL_TO_REFRESH -> PullToRefresh()
|
||||
else -> throw IllegalArgumentException("Unknown page: $page")
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -1,12 +1,15 @@
|
||||
package app.k9mail.ui.catalog.ui.molecule
|
||||
|
||||
import app.k9mail.ui.catalog.ui.CatalogPage
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
enum class CatalogMoleculePage(
|
||||
private val displayName: String,
|
||||
) {
|
||||
override val displayName: String,
|
||||
override val isFullScreen: Boolean = false,
|
||||
) : CatalogPage {
|
||||
INPUT("Inputs"),
|
||||
STATE("States"),
|
||||
PULL_TO_REFRESH("Pull to refresh", isFullScreen = true),
|
||||
;
|
||||
|
||||
override fun toString(): String {
|
||||
|
@ -0,0 +1,47 @@
|
||||
package app.k9mail.ui.catalog.ui.molecule.items
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.text.TextTitleMedium
|
||||
import app.k9mail.core.ui.compose.designsystem.molecule.PullToRefreshBox
|
||||
import app.k9mail.core.ui.compose.theme2.MainTheme
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Composable
|
||||
fun PullToRefresh(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val isRefreshing = remember { mutableStateOf(false) }
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
PullToRefreshBox(
|
||||
isRefreshing = isRefreshing.value,
|
||||
onRefresh = {
|
||||
isRefreshing.value = true
|
||||
|
||||
coroutineScope.launch {
|
||||
delay(2000)
|
||||
isRefreshing.value = false
|
||||
}
|
||||
},
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
) {
|
||||
LazyColumn(
|
||||
verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.double),
|
||||
) {
|
||||
items(10) {
|
||||
TextTitleMedium(text = "Item $it")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -17,9 +17,11 @@ fun CatalogOrganismContent(
|
||||
pages = pages,
|
||||
initialPage = initialPage,
|
||||
modifier = modifier,
|
||||
) {
|
||||
when (it) {
|
||||
APP_BAR -> appBarItems()
|
||||
}
|
||||
}
|
||||
onRenderPage = {
|
||||
when (it) {
|
||||
APP_BAR -> appBarItems()
|
||||
}
|
||||
},
|
||||
onRenderFullScreenPage = {},
|
||||
)
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
package app.k9mail.ui.catalog.ui.organism
|
||||
|
||||
import app.k9mail.ui.catalog.ui.CatalogPage
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
enum class CatalogOrganismPage(
|
||||
private val displayName: String,
|
||||
) {
|
||||
override val displayName: String,
|
||||
override val isFullScreen: Boolean = false,
|
||||
) : CatalogPage {
|
||||
APP_BAR("App Bars"),
|
||||
;
|
||||
|
||||
|
@ -0,0 +1,45 @@
|
||||
package app.k9mail.core.ui.compose.designsystem.molecule
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.Surface
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodyLarge
|
||||
import app.k9mail.core.ui.compose.theme2.MainTheme
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun PullToRefreshBoxPreview() {
|
||||
PreviewWithThemes {
|
||||
PullToRefreshBox(
|
||||
isRefreshing = false,
|
||||
onRefresh = {},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
.height(MainTheme.sizes.medium),
|
||||
) {
|
||||
Surface {
|
||||
TextBodyLarge("Pull to refresh")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun PullToRefreshBoxRefreshingPreview() {
|
||||
PreviewWithThemes {
|
||||
PullToRefreshBox(
|
||||
isRefreshing = true,
|
||||
onRefresh = {},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
.height(MainTheme.sizes.medium),
|
||||
) {
|
||||
Surface {
|
||||
TextBodyLarge("Refreshing ...")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
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
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun NavigationDrawerItemBadgePreview() {
|
||||
PreviewWithThemes {
|
||||
NavigationDrawerItemBadge(
|
||||
label = "100+",
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
package app.k9mail.core.ui.compose.designsystem.organism.drawer
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.AccountBox
|
||||
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.Icon
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.text.TextLabelLarge
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun NavigationDrawerItemSelectedPreview() {
|
||||
PreviewWithThemes {
|
||||
NavigationDrawerItem(
|
||||
label = "DrawerItem",
|
||||
selected = true,
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun NavigationDrawerItemUnselectedPreview() {
|
||||
PreviewWithThemes {
|
||||
NavigationDrawerItem(
|
||||
label = "DrawerItem",
|
||||
selected = false,
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun NavigationDrawerItemWithIconPreview() {
|
||||
PreviewWithThemes {
|
||||
NavigationDrawerItem(
|
||||
label = "DrawerItem",
|
||||
selected = false,
|
||||
onClick = {},
|
||||
icon = {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.AccountBox,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun NavigationDrawerItemWithLabelBadgePreview() {
|
||||
PreviewWithThemes {
|
||||
NavigationDrawerItem(
|
||||
label = "DrawerItem",
|
||||
selected = false,
|
||||
onClick = {},
|
||||
badge = {
|
||||
TextLabelLarge(
|
||||
text = "100+",
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package app.k9mail.core.ui.compose.designsystem.molecule
|
||||
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults.Indicator
|
||||
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox as Material3PullToRefreshBox
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun PullToRefreshBox(
|
||||
isRefreshing: Boolean,
|
||||
onRefresh: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
contentAlignment: Alignment = Alignment.TopStart,
|
||||
content: @Composable BoxScope.() -> Unit,
|
||||
) {
|
||||
val state = rememberPullToRefreshState()
|
||||
|
||||
Material3PullToRefreshBox(
|
||||
isRefreshing = isRefreshing,
|
||||
onRefresh = onRefresh,
|
||||
modifier = modifier
|
||||
.testTag("PullToRefreshBox"),
|
||||
state = state,
|
||||
contentAlignment = contentAlignment,
|
||||
indicator = {
|
||||
Indicator(
|
||||
modifier = Modifier.align(Alignment.TopCenter)
|
||||
.testTag("PullToRefreshIndicator"),
|
||||
isRefreshing = isRefreshing,
|
||||
state = state,
|
||||
)
|
||||
},
|
||||
content = content,
|
||||
)
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package app.k9mail.core.ui.compose.designsystem.organism.drawer
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.text.TextLabelLarge
|
||||
|
||||
@Composable
|
||||
fun NavigationDrawerItemBadge(
|
||||
label: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
TextLabelLarge(
|
||||
text = label,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package app.k9mail.core.ui.compose.testing
|
||||
|
||||
import app.k9mail.core.ui.compose.common.mvi.BaseViewModel
|
||||
|
||||
/**
|
||||
* Base class for providing fake MVI ViewModels for testing.
|
||||
*
|
||||
* This class provides a way to capture events and emit effects on a fake ViewModel.
|
||||
* The state can be set directly using [applyState].
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* ```
|
||||
* class FakeViewModel(
|
||||
* initialState: State = State(),
|
||||
* ) : BaseFakeViewModel<State, Event, Effect>(initialState), ViewModel
|
||||
* ```
|
||||
*/
|
||||
abstract class BaseFakeViewModel<STATE, EVENT, EFFECT>(
|
||||
initialState: STATE,
|
||||
) : BaseViewModel<STATE, EVENT, EFFECT>(initialState = initialState) {
|
||||
|
||||
val events = mutableListOf<EVENT>()
|
||||
|
||||
override fun event(event: EVENT) {
|
||||
events.add(event)
|
||||
}
|
||||
|
||||
fun effect(effect: EFFECT) {
|
||||
emitEffect(effect)
|
||||
}
|
||||
|
||||
fun applyState(state: STATE) {
|
||||
updateState { state }
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
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
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun DrawerContentPreview() {
|
||||
PreviewWithTheme {
|
||||
DrawerContent()
|
||||
}
|
||||
}
|
@ -1,17 +1,13 @@
|
||||
package app.k9mail.feature.navigation.drawer
|
||||
|
||||
import android.view.Surface
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.core.view.GravityCompat
|
||||
import androidx.drawerlayout.widget.DrawerLayout
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.Surface
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.text.TextBodyLarge
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import app.k9mail.core.ui.theme.api.FeatureThemeProvider
|
||||
import app.k9mail.feature.navigation.drawer.ui.DrawerView
|
||||
import app.k9mail.legacy.account.Account
|
||||
import com.mikepenz.materialdrawer.widget.MaterialDrawerSliderView
|
||||
import org.koin.core.component.KoinComponent
|
||||
@ -26,19 +22,16 @@ class FolderDrawer(
|
||||
private val drawer: DrawerLayout = parent.findViewById(R.id.navigation_drawer_layout)
|
||||
private val drawerView: ComposeView = parent.findViewById(R.id.material_drawer_compose_view)
|
||||
private val sliderView: MaterialDrawerSliderView = parent.findViewById(R.id.material_drawer_slider)
|
||||
private val swipeRefreshLayout: SwipeRefreshLayout = parent.findViewById(R.id.material_drawer_swipe_refresh)
|
||||
|
||||
init {
|
||||
sliderView.visibility = View.GONE
|
||||
drawerView.visibility = View.VISIBLE
|
||||
swipeRefreshLayout.isEnabled = false
|
||||
|
||||
drawerView.setContent {
|
||||
themeProvider.WithTheme {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = Color.Red,
|
||||
) {
|
||||
TextBodyLarge("Folder Drawer")
|
||||
}
|
||||
DrawerView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,11 @@
|
||||
package app.k9mail.feature.navigation.drawer
|
||||
|
||||
import app.k9mail.feature.navigation.drawer.ui.DrawerViewModel
|
||||
import org.koin.androidx.viewmodel.dsl.viewModel
|
||||
import org.koin.core.module.Module
|
||||
import org.koin.dsl.module
|
||||
|
||||
val navigationDrawerModule: Module = module {
|
||||
|
||||
viewModel { DrawerViewModel() }
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package app.k9mail.feature.navigation.drawer.ui
|
||||
|
||||
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.Surface
|
||||
import app.k9mail.core.ui.compose.designsystem.organism.drawer.NavigationDrawerItem
|
||||
import app.k9mail.core.ui.compose.theme2.MainTheme
|
||||
|
||||
@Composable
|
||||
fun DrawerContent(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Surface(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.testTag("DrawerContent"),
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(
|
||||
vertical = MainTheme.spacings.oneHalf,
|
||||
),
|
||||
) {
|
||||
item {
|
||||
NavigationDrawerItem(
|
||||
label = "Folder1",
|
||||
selected = true,
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
item {
|
||||
NavigationDrawerItem(
|
||||
label = "Folder2",
|
||||
selected = false,
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
item {
|
||||
NavigationDrawerItem(
|
||||
label = "Folder3",
|
||||
selected = false,
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package app.k9mail.feature.navigation.drawer.ui
|
||||
|
||||
import app.k9mail.core.ui.compose.common.mvi.UnidirectionalViewModel
|
||||
|
||||
interface DrawerContract {
|
||||
|
||||
interface ViewModel : UnidirectionalViewModel<State, Event, Effect>
|
||||
|
||||
data class State(
|
||||
val isLoading: Boolean = false,
|
||||
)
|
||||
|
||||
sealed interface Event {
|
||||
data object OnRefresh : Event
|
||||
}
|
||||
|
||||
sealed interface Effect
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
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
|
||||
|
||||
@Composable
|
||||
fun DrawerView(
|
||||
viewModel: DrawerContract.ViewModel = koinViewModel<DrawerViewModel>(),
|
||||
) {
|
||||
val (state, dispatch) = viewModel.observe { }
|
||||
|
||||
PullToRefreshBox(
|
||||
isRefreshing = state.value.isLoading,
|
||||
onRefresh = { dispatch(DrawerContract.Event.OnRefresh) },
|
||||
) {
|
||||
DrawerContent()
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
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.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.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
class DrawerViewModel(
|
||||
initialState: State = State(),
|
||||
) : BaseViewModel<State, Event, Effect>(
|
||||
initialState = initialState,
|
||||
),
|
||||
ViewModel {
|
||||
|
||||
override fun event(event: Event) {
|
||||
when (event) {
|
||||
Event.OnRefresh -> refresh()
|
||||
}
|
||||
}
|
||||
|
||||
private fun refresh() {
|
||||
if (state.value.isLoading) {
|
||||
return
|
||||
}
|
||||
viewModelScope.launch {
|
||||
updateState {
|
||||
it.copy(isLoading = true)
|
||||
}
|
||||
|
||||
// TODO: replace with actual data loading
|
||||
delay(500)
|
||||
|
||||
updateState {
|
||||
it.copy(isLoading = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package app.k9mail.feature.navigation.drawer.ui
|
||||
|
||||
import androidx.compose.ui.test.onChildAt
|
||||
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.State
|
||||
import kotlin.test.Test
|
||||
import kotlinx.coroutines.test.runTest
|
||||
|
||||
class DrawerViewKtTest : ComposeTest() {
|
||||
|
||||
@Test
|
||||
fun `pull refresh should listen to view model state`() = runTest {
|
||||
val initialState = State(
|
||||
isLoading = false,
|
||||
)
|
||||
val viewModel = FakeDrawerViewModel(initialState)
|
||||
|
||||
setContentWithTheme {
|
||||
DrawerView(
|
||||
viewModel = viewModel,
|
||||
)
|
||||
}
|
||||
|
||||
onNodeWithTag("PullToRefreshBox").assertExists()
|
||||
onNodeWithTag("PullToRefreshIndicator").assertExists()
|
||||
.onChildAt(0).assertExists()
|
||||
.printToString()
|
||||
.contains("ProgressBarRangeInfo(current=0.0, range=0.0..1.0, steps=0)")
|
||||
|
||||
viewModel.applyState(initialState.copy(isLoading = true))
|
||||
|
||||
onNodeWithTag("PullToRefreshIndicator").assertExists()
|
||||
.onChildAt(0).assertExists()
|
||||
.printToString()
|
||||
.contains("ProgressBarRangeInfo(current=0.0, range=0.0..0.0, steps=0)")
|
||||
|
||||
viewModel.applyState(initialState.copy(isLoading = false))
|
||||
|
||||
onNodeWithTag("PullToRefreshIndicator").assertExists()
|
||||
.onChildAt(0).assertExists()
|
||||
.printToString()
|
||||
.contains("ProgressBarRangeInfo(current=0.0, range=0.0..1.0, steps=0)")
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
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.ui.DrawerContract.Event
|
||||
import app.k9mail.feature.navigation.drawer.ui.DrawerContract.State
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import kotlin.test.Test
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.advanceUntilIdle
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class DrawerViewModelTest {
|
||||
|
||||
@get:Rule
|
||||
val mainDispatcherRule = MainDispatcherRule()
|
||||
|
||||
private val testSubject = DrawerViewModel()
|
||||
|
||||
@Test
|
||||
fun `should change loading state when OnRefresh event is received`() = runTest {
|
||||
eventStateTest(
|
||||
viewModel = testSubject,
|
||||
initialState = State(isLoading = false),
|
||||
event = Event.OnRefresh,
|
||||
expectedState = State(isLoading = true),
|
||||
coroutineScope = backgroundScope,
|
||||
)
|
||||
|
||||
advanceUntilIdle()
|
||||
|
||||
assertThat(testSubject.state.value.isLoading).isEqualTo(false)
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package app.k9mail.feature.navigation.drawer.ui
|
||||
|
||||
import app.k9mail.core.ui.compose.testing.BaseFakeViewModel
|
||||
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
|
||||
|
||||
class FakeDrawerViewModel(
|
||||
initialState: State = State(),
|
||||
) : BaseFakeViewModel<State, Event, Effect>(initialState), ViewModel
|
@ -1,9 +1,12 @@
|
||||
package com.fsck.k9.ui.messagelist
|
||||
|
||||
import app.k9mail.feature.navigation.drawer.navigationDrawerModule
|
||||
import org.koin.androidx.viewmodel.dsl.viewModel
|
||||
import org.koin.dsl.module
|
||||
|
||||
val messageListUiModule = module {
|
||||
includes(navigationDrawerModule)
|
||||
|
||||
viewModel { MessageListViewModel(get()) }
|
||||
factory { DefaultFolderProvider() }
|
||||
factory {
|
||||
|
Loading…
Reference in New Issue
Block a user