Add Stable interface for Category state (#7539)

This commit is contained in:
Andreas 2022-07-15 23:35:19 +02:00 committed by GitHub
parent 83e193f1ab
commit a21aa8125e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 80 additions and 34 deletions

View file

@ -7,7 +7,6 @@ import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarScrollState import androidx.compose.material3.rememberTopAppBarScrollState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
@ -19,6 +18,7 @@ import eu.kanade.presentation.category.components.CategoryFloatingActionButton
import eu.kanade.presentation.category.components.CategoryRenameDialog import eu.kanade.presentation.category.components.CategoryRenameDialog
import eu.kanade.presentation.category.components.CategoryTopAppBar import eu.kanade.presentation.category.components.CategoryTopAppBar
import eu.kanade.presentation.components.EmptyScreen import eu.kanade.presentation.components.EmptyScreen
import eu.kanade.presentation.components.LoadingScreen
import eu.kanade.presentation.components.Scaffold import eu.kanade.presentation.components.Scaffold
import eu.kanade.presentation.util.horizontalPadding import eu.kanade.presentation.util.horizontalPadding
import eu.kanade.presentation.util.plus import eu.kanade.presentation.util.plus
@ -50,25 +50,25 @@ fun CategoryScreen(
floatingActionButton = { floatingActionButton = {
CategoryFloatingActionButton( CategoryFloatingActionButton(
lazyListState = lazyListState, lazyListState = lazyListState,
onCreate = { presenter.dialog = CategoryPresenter.Dialog.Create }, onCreate = { presenter.dialog = Dialog.Create },
) )
}, },
) { paddingValues -> ) { paddingValues ->
val context = LocalContext.current val context = LocalContext.current
val categories by presenter.categories.collectAsState(initial = emptyList()) when {
if (categories.isEmpty()) { presenter.isLoading -> LoadingScreen()
EmptyScreen(textResource = R.string.information_empty_category) presenter.isEmpty -> EmptyScreen(textResource = R.string.information_empty_category)
} else { else -> {
CategoryContent( CategoryContent(
categories = categories, state = presenter,
lazyListState = lazyListState, lazyListState = lazyListState,
paddingValues = paddingValues + topPaddingValues + PaddingValues(horizontal = horizontalPadding), paddingValues = paddingValues + topPaddingValues + PaddingValues(horizontal = horizontalPadding),
onMoveUp = { presenter.moveUp(it) }, onMoveUp = { presenter.moveUp(it) },
onMoveDown = { presenter.moveDown(it) }, onMoveDown = { presenter.moveDown(it) },
onRename = { presenter.dialog = Dialog.Rename(it) }, )
onDelete = { presenter.dialog = Dialog.Delete(it) }, }
)
} }
val onDismissRequest = { presenter.dialog = null } val onDismissRequest = { presenter.dialog = null }
when (val dialog = presenter.dialog) { when (val dialog = presenter.dialog) {
Dialog.Create -> { Dialog.Create -> {

View file

@ -0,0 +1,28 @@
package eu.kanade.presentation.category
import androidx.compose.runtime.Stable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import eu.kanade.domain.category.model.Category
import eu.kanade.tachiyomi.ui.category.CategoryPresenter
@Stable
interface CategoryState {
val isLoading: Boolean
var dialog: CategoryPresenter.Dialog?
val categories: List<Category>
val isEmpty: Boolean
}
fun CategoryState(): CategoryState {
return CategoryStateImpl()
}
class CategoryStateImpl : CategoryState {
override var isLoading: Boolean by mutableStateOf(true)
override var dialog: CategoryPresenter.Dialog? by mutableStateOf(null)
override var categories: List<Category> by mutableStateOf(emptyList())
override val isEmpty: Boolean by derivedStateOf { categories.isEmpty() }
}

View file

@ -5,34 +5,40 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import eu.kanade.domain.category.model.Category import eu.kanade.domain.category.model.Category
import eu.kanade.presentation.category.CategoryState
import eu.kanade.presentation.components.LazyColumn import eu.kanade.presentation.components.LazyColumn
import eu.kanade.tachiyomi.ui.category.CategoryPresenter.Dialog
@Composable @Composable
fun CategoryContent( fun CategoryContent(
categories: List<Category>, state: CategoryState,
lazyListState: LazyListState, lazyListState: LazyListState,
paddingValues: PaddingValues, paddingValues: PaddingValues,
onMoveUp: (Category) -> Unit, onMoveUp: (Category) -> Unit,
onMoveDown: (Category) -> Unit, onMoveDown: (Category) -> Unit,
onRename: (Category) -> Unit,
onDelete: (Category) -> Unit,
) { ) {
val categories = state.categories
LazyColumn( LazyColumn(
state = lazyListState, state = lazyListState,
contentPadding = paddingValues, contentPadding = paddingValues,
verticalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp),
) { ) {
itemsIndexed(categories) { index, category -> itemsIndexed(
items = categories,
key = { _, category -> category.id },
) { index, category ->
CategoryListItem( CategoryListItem(
modifier = Modifier.animateItemPlacement(),
category = category, category = category,
canMoveUp = index != 0, canMoveUp = index != 0,
canMoveDown = index != categories.lastIndex, canMoveDown = index != categories.lastIndex,
onMoveUp = onMoveUp, onMoveUp = onMoveUp,
onMoveDown = onMoveDown, onMoveDown = onMoveDown,
onRename = onRename, onRename = { state.dialog = Dialog.Rename(category) },
onDelete = onDelete, onDelete = { state.dialog = Dialog.Delete(category) },
) )
} }
} }

View file

@ -21,15 +21,18 @@ import eu.kanade.presentation.util.horizontalPadding
@Composable @Composable
fun CategoryListItem( fun CategoryListItem(
modifier: Modifier = Modifier,
category: Category, category: Category,
canMoveUp: Boolean, canMoveUp: Boolean,
canMoveDown: Boolean, canMoveDown: Boolean,
onMoveUp: (Category) -> Unit, onMoveUp: (Category) -> Unit,
onMoveDown: (Category) -> Unit, onMoveDown: (Category) -> Unit,
onRename: (Category) -> Unit, onRename: () -> Unit,
onDelete: (Category) -> Unit, onDelete: () -> Unit,
) { ) {
ElevatedCard { ElevatedCard(
modifier = modifier,
) {
Row( Row(
modifier = Modifier modifier = Modifier
.padding(start = horizontalPadding, top = horizontalPadding, end = horizontalPadding), .padding(start = horizontalPadding, top = horizontalPadding, end = horizontalPadding),
@ -52,10 +55,10 @@ fun CategoryListItem(
Icon(imageVector = Icons.Outlined.ArrowDropDown, contentDescription = "") Icon(imageVector = Icons.Outlined.ArrowDropDown, contentDescription = "")
} }
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
IconButton(onClick = { onRename(category) }) { IconButton(onClick = onRename) {
Icon(imageVector = Icons.Outlined.Edit, contentDescription = "") Icon(imageVector = Icons.Outlined.Edit, contentDescription = "")
} }
IconButton(onClick = { onDelete(category) }) { IconButton(onClick = onDelete) {
Icon(imageVector = Icons.Outlined.Delete, contentDescription = "") Icon(imageVector = Icons.Outlined.Delete, contentDescription = "")
} }
} }

View file

@ -1,36 +1,45 @@
package eu.kanade.tachiyomi.ui.category package eu.kanade.tachiyomi.ui.category
import androidx.compose.runtime.getValue import android.os.Bundle
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import eu.kanade.domain.category.interactor.CreateCategoryWithName import eu.kanade.domain.category.interactor.CreateCategoryWithName
import eu.kanade.domain.category.interactor.DeleteCategory import eu.kanade.domain.category.interactor.DeleteCategory
import eu.kanade.domain.category.interactor.GetCategories import eu.kanade.domain.category.interactor.GetCategories
import eu.kanade.domain.category.interactor.RenameCategory import eu.kanade.domain.category.interactor.RenameCategory
import eu.kanade.domain.category.interactor.ReorderCategory import eu.kanade.domain.category.interactor.ReorderCategory
import eu.kanade.domain.category.model.Category import eu.kanade.domain.category.model.Category
import eu.kanade.presentation.category.CategoryState
import eu.kanade.presentation.category.CategoryStateImpl
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.launchIO
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.consumeAsFlow import kotlinx.coroutines.flow.consumeAsFlow
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
class CategoryPresenter( class CategoryPresenter(
private val state: CategoryStateImpl = CategoryState() as CategoryStateImpl,
private val getCategories: GetCategories = Injekt.get(), private val getCategories: GetCategories = Injekt.get(),
private val createCategoryWithName: CreateCategoryWithName = Injekt.get(), private val createCategoryWithName: CreateCategoryWithName = Injekt.get(),
private val renameCategory: RenameCategory = Injekt.get(), private val renameCategory: RenameCategory = Injekt.get(),
private val reorderCategory: ReorderCategory = Injekt.get(), private val reorderCategory: ReorderCategory = Injekt.get(),
private val deleteCategory: DeleteCategory = Injekt.get(), private val deleteCategory: DeleteCategory = Injekt.get(),
) : BasePresenter<CategoryController>() { ) : BasePresenter<CategoryController>(), CategoryState by state {
var dialog: Dialog? by mutableStateOf(null)
val categories = getCategories.subscribe()
private val _events: Channel<Event> = Channel(Int.MAX_VALUE) private val _events: Channel<Event> = Channel(Int.MAX_VALUE)
val events = _events.consumeAsFlow() val events = _events.consumeAsFlow()
override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState)
presenterScope.launchIO {
getCategories.subscribe()
.collectLatest {
state.isLoading = false
state.categories = it
}
}
}
fun createCategory(name: String) { fun createCategory(name: String) {
presenterScope.launchIO { presenterScope.launchIO {
when (createCategoryWithName.await(name)) { when (createCategoryWithName.await(name)) {