mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-31 14:27:57 +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