mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-03 23:58:55 +01:00 
			
		
		
		
	Add Stable interface for Category state (#7539)
This commit is contained in:
		@@ -7,7 +7,6 @@ import androidx.compose.material3.TopAppBarDefaults
 | 
			
		||||
import androidx.compose.material3.rememberTopAppBarScrollState
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.runtime.LaunchedEffect
 | 
			
		||||
import androidx.compose.runtime.collectAsState
 | 
			
		||||
import androidx.compose.runtime.getValue
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
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.CategoryTopAppBar
 | 
			
		||||
import eu.kanade.presentation.components.EmptyScreen
 | 
			
		||||
import eu.kanade.presentation.components.LoadingScreen
 | 
			
		||||
import eu.kanade.presentation.components.Scaffold
 | 
			
		||||
import eu.kanade.presentation.util.horizontalPadding
 | 
			
		||||
import eu.kanade.presentation.util.plus
 | 
			
		||||
@@ -50,25 +50,25 @@ fun CategoryScreen(
 | 
			
		||||
        floatingActionButton = {
 | 
			
		||||
            CategoryFloatingActionButton(
 | 
			
		||||
                lazyListState = lazyListState,
 | 
			
		||||
                onCreate = { presenter.dialog = CategoryPresenter.Dialog.Create },
 | 
			
		||||
                onCreate = { presenter.dialog = Dialog.Create },
 | 
			
		||||
            )
 | 
			
		||||
        },
 | 
			
		||||
    ) { paddingValues ->
 | 
			
		||||
        val context = LocalContext.current
 | 
			
		||||
        val categories by presenter.categories.collectAsState(initial = emptyList())
 | 
			
		||||
        if (categories.isEmpty()) {
 | 
			
		||||
            EmptyScreen(textResource = R.string.information_empty_category)
 | 
			
		||||
        } else {
 | 
			
		||||
            CategoryContent(
 | 
			
		||||
                categories = categories,
 | 
			
		||||
                lazyListState = lazyListState,
 | 
			
		||||
                paddingValues = paddingValues + topPaddingValues + PaddingValues(horizontal = horizontalPadding),
 | 
			
		||||
                onMoveUp = { presenter.moveUp(it) },
 | 
			
		||||
                onMoveDown = { presenter.moveDown(it) },
 | 
			
		||||
                onRename = { presenter.dialog = Dialog.Rename(it) },
 | 
			
		||||
                onDelete = { presenter.dialog = Dialog.Delete(it) },
 | 
			
		||||
            )
 | 
			
		||||
        when {
 | 
			
		||||
            presenter.isLoading -> LoadingScreen()
 | 
			
		||||
            presenter.isEmpty -> EmptyScreen(textResource = R.string.information_empty_category)
 | 
			
		||||
            else -> {
 | 
			
		||||
                CategoryContent(
 | 
			
		||||
                    state = presenter,
 | 
			
		||||
                    lazyListState = lazyListState,
 | 
			
		||||
                    paddingValues = paddingValues + topPaddingValues + PaddingValues(horizontal = horizontalPadding),
 | 
			
		||||
                    onMoveUp = { presenter.moveUp(it) },
 | 
			
		||||
                    onMoveDown = { presenter.moveDown(it) },
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val onDismissRequest = { presenter.dialog = null }
 | 
			
		||||
        when (val dialog = presenter.dialog) {
 | 
			
		||||
            Dialog.Create -> {
 | 
			
		||||
 
 | 
			
		||||
@@ -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() }
 | 
			
		||||
}
 | 
			
		||||
@@ -5,34 +5,40 @@ import androidx.compose.foundation.layout.PaddingValues
 | 
			
		||||
import androidx.compose.foundation.lazy.LazyListState
 | 
			
		||||
import androidx.compose.foundation.lazy.itemsIndexed
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import eu.kanade.domain.category.model.Category
 | 
			
		||||
import eu.kanade.presentation.category.CategoryState
 | 
			
		||||
import eu.kanade.presentation.components.LazyColumn
 | 
			
		||||
import eu.kanade.tachiyomi.ui.category.CategoryPresenter.Dialog
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun CategoryContent(
 | 
			
		||||
    categories: List<Category>,
 | 
			
		||||
    state: CategoryState,
 | 
			
		||||
    lazyListState: LazyListState,
 | 
			
		||||
    paddingValues: PaddingValues,
 | 
			
		||||
    onMoveUp: (Category) -> Unit,
 | 
			
		||||
    onMoveDown: (Category) -> Unit,
 | 
			
		||||
    onRename: (Category) -> Unit,
 | 
			
		||||
    onDelete: (Category) -> Unit,
 | 
			
		||||
) {
 | 
			
		||||
    val categories = state.categories
 | 
			
		||||
    LazyColumn(
 | 
			
		||||
        state = lazyListState,
 | 
			
		||||
        contentPadding = paddingValues,
 | 
			
		||||
        verticalArrangement = Arrangement.spacedBy(8.dp),
 | 
			
		||||
    ) {
 | 
			
		||||
        itemsIndexed(categories) { index, category ->
 | 
			
		||||
        itemsIndexed(
 | 
			
		||||
            items = categories,
 | 
			
		||||
            key = { _, category -> category.id },
 | 
			
		||||
        ) { index, category ->
 | 
			
		||||
            CategoryListItem(
 | 
			
		||||
                modifier = Modifier.animateItemPlacement(),
 | 
			
		||||
                category = category,
 | 
			
		||||
                canMoveUp = index != 0,
 | 
			
		||||
                canMoveDown = index != categories.lastIndex,
 | 
			
		||||
                onMoveUp = onMoveUp,
 | 
			
		||||
                onMoveDown = onMoveDown,
 | 
			
		||||
                onRename = onRename,
 | 
			
		||||
                onDelete = onDelete,
 | 
			
		||||
                onRename = { state.dialog = Dialog.Rename(category) },
 | 
			
		||||
                onDelete = { state.dialog = Dialog.Delete(category) },
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -21,15 +21,18 @@ import eu.kanade.presentation.util.horizontalPadding
 | 
			
		||||
 | 
			
		||||
@Composable
 | 
			
		||||
fun CategoryListItem(
 | 
			
		||||
    modifier: Modifier = Modifier,
 | 
			
		||||
    category: Category,
 | 
			
		||||
    canMoveUp: Boolean,
 | 
			
		||||
    canMoveDown: Boolean,
 | 
			
		||||
    onMoveUp: (Category) -> Unit,
 | 
			
		||||
    onMoveDown: (Category) -> Unit,
 | 
			
		||||
    onRename: (Category) -> Unit,
 | 
			
		||||
    onDelete: (Category) -> Unit,
 | 
			
		||||
    onRename: () -> Unit,
 | 
			
		||||
    onDelete: () -> Unit,
 | 
			
		||||
) {
 | 
			
		||||
    ElevatedCard {
 | 
			
		||||
    ElevatedCard(
 | 
			
		||||
        modifier = modifier,
 | 
			
		||||
    ) {
 | 
			
		||||
        Row(
 | 
			
		||||
            modifier = Modifier
 | 
			
		||||
                .padding(start = horizontalPadding, top = horizontalPadding, end = horizontalPadding),
 | 
			
		||||
@@ -52,10 +55,10 @@ fun CategoryListItem(
 | 
			
		||||
                Icon(imageVector = Icons.Outlined.ArrowDropDown, contentDescription = "")
 | 
			
		||||
            }
 | 
			
		||||
            Spacer(modifier = Modifier.weight(1f))
 | 
			
		||||
            IconButton(onClick = { onRename(category) }) {
 | 
			
		||||
            IconButton(onClick = onRename) {
 | 
			
		||||
                Icon(imageVector = Icons.Outlined.Edit, contentDescription = "")
 | 
			
		||||
            }
 | 
			
		||||
            IconButton(onClick = { onDelete(category) }) {
 | 
			
		||||
            IconButton(onClick = onDelete) {
 | 
			
		||||
                Icon(imageVector = Icons.Outlined.Delete, contentDescription = "")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,36 +1,45 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.category
 | 
			
		||||
 | 
			
		||||
import androidx.compose.runtime.getValue
 | 
			
		||||
import androidx.compose.runtime.mutableStateOf
 | 
			
		||||
import androidx.compose.runtime.setValue
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import eu.kanade.domain.category.interactor.CreateCategoryWithName
 | 
			
		||||
import eu.kanade.domain.category.interactor.DeleteCategory
 | 
			
		||||
import eu.kanade.domain.category.interactor.GetCategories
 | 
			
		||||
import eu.kanade.domain.category.interactor.RenameCategory
 | 
			
		||||
import eu.kanade.domain.category.interactor.ReorderCategory
 | 
			
		||||
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.util.lang.launchIO
 | 
			
		||||
import kotlinx.coroutines.channels.Channel
 | 
			
		||||
import kotlinx.coroutines.flow.collectLatest
 | 
			
		||||
import kotlinx.coroutines.flow.consumeAsFlow
 | 
			
		||||
import uy.kohesive.injekt.Injekt
 | 
			
		||||
import uy.kohesive.injekt.api.get
 | 
			
		||||
 | 
			
		||||
class CategoryPresenter(
 | 
			
		||||
    private val state: CategoryStateImpl = CategoryState() as CategoryStateImpl,
 | 
			
		||||
    private val getCategories: GetCategories = Injekt.get(),
 | 
			
		||||
    private val createCategoryWithName: CreateCategoryWithName = Injekt.get(),
 | 
			
		||||
    private val renameCategory: RenameCategory = Injekt.get(),
 | 
			
		||||
    private val reorderCategory: ReorderCategory = Injekt.get(),
 | 
			
		||||
    private val deleteCategory: DeleteCategory = Injekt.get(),
 | 
			
		||||
) : BasePresenter<CategoryController>() {
 | 
			
		||||
 | 
			
		||||
    var dialog: Dialog? by mutableStateOf(null)
 | 
			
		||||
 | 
			
		||||
    val categories = getCategories.subscribe()
 | 
			
		||||
) : BasePresenter<CategoryController>(), CategoryState by state {
 | 
			
		||||
 | 
			
		||||
    private val _events: Channel<Event> = Channel(Int.MAX_VALUE)
 | 
			
		||||
    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) {
 | 
			
		||||
        presenterScope.launchIO {
 | 
			
		||||
            when (createCategoryWithName.await(name)) {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user