mirror of
https://github.com/mihonapp/mihon.git
synced 2024-12-01 18:32:24 -05:00
Use Voyager on Category screen (#8472)
This commit is contained in:
parent
9c9357639a
commit
bf9edda04c
7 changed files with 253 additions and 204 deletions
|
@ -4,31 +4,28 @@ import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import eu.kanade.domain.category.model.Category
|
||||||
import eu.kanade.presentation.category.components.CategoryContent
|
import eu.kanade.presentation.category.components.CategoryContent
|
||||||
import eu.kanade.presentation.category.components.CategoryCreateDialog
|
|
||||||
import eu.kanade.presentation.category.components.CategoryDeleteDialog
|
|
||||||
import eu.kanade.presentation.category.components.CategoryFloatingActionButton
|
import eu.kanade.presentation.category.components.CategoryFloatingActionButton
|
||||||
import eu.kanade.presentation.category.components.CategoryRenameDialog
|
|
||||||
import eu.kanade.presentation.components.AppBar
|
import eu.kanade.presentation.components.AppBar
|
||||||
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
|
||||||
import eu.kanade.presentation.util.topPaddingValues
|
import eu.kanade.presentation.util.topPaddingValues
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.ui.category.CategoryPresenter
|
import eu.kanade.tachiyomi.ui.category.CategoryScreenState
|
||||||
import eu.kanade.tachiyomi.ui.category.CategoryPresenter.Dialog
|
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CategoryScreen(
|
fun CategoryScreen(
|
||||||
presenter: CategoryPresenter,
|
state: CategoryScreenState.Success,
|
||||||
|
onClickCreate: () -> Unit,
|
||||||
|
onClickRename: (Category) -> Unit,
|
||||||
|
onClickDelete: (Category) -> Unit,
|
||||||
|
onClickMoveUp: (Category) -> Unit,
|
||||||
|
onClickMoveDown: (Category) -> Unit,
|
||||||
navigateUp: () -> Unit,
|
navigateUp: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val lazyListState = rememberLazyListState()
|
val lazyListState = rememberLazyListState()
|
||||||
|
@ -43,63 +40,26 @@ fun CategoryScreen(
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
CategoryFloatingActionButton(
|
CategoryFloatingActionButton(
|
||||||
lazyListState = lazyListState,
|
lazyListState = lazyListState,
|
||||||
onCreate = { presenter.dialog = Dialog.Create },
|
onCreate = onClickCreate,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
val context = LocalContext.current
|
if (state.isEmpty) {
|
||||||
when {
|
EmptyScreen(
|
||||||
presenter.isLoading -> LoadingScreen()
|
|
||||||
presenter.isEmpty -> EmptyScreen(
|
|
||||||
textResource = R.string.information_empty_category,
|
textResource = R.string.information_empty_category,
|
||||||
modifier = Modifier.padding(paddingValues),
|
modifier = Modifier.padding(paddingValues),
|
||||||
)
|
)
|
||||||
else -> {
|
return@Scaffold
|
||||||
CategoryContent(
|
|
||||||
state = presenter,
|
|
||||||
lazyListState = lazyListState,
|
|
||||||
paddingValues = paddingValues + topPaddingValues + PaddingValues(horizontal = horizontalPadding),
|
|
||||||
onMoveUp = { presenter.moveUp(it) },
|
|
||||||
onMoveDown = { presenter.moveDown(it) },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val onDismissRequest = { presenter.dialog = null }
|
CategoryContent(
|
||||||
when (val dialog = presenter.dialog) {
|
categories = state.categories,
|
||||||
Dialog.Create -> {
|
lazyListState = lazyListState,
|
||||||
CategoryCreateDialog(
|
paddingValues = paddingValues + topPaddingValues + PaddingValues(horizontal = horizontalPadding),
|
||||||
onDismissRequest = onDismissRequest,
|
onClickRename = onClickRename,
|
||||||
onCreate = { presenter.createCategory(it) },
|
onClickDelete = onClickDelete,
|
||||||
|
onMoveUp = onClickMoveUp,
|
||||||
|
onMoveDown = onClickMoveDown,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is Dialog.Rename -> {
|
|
||||||
CategoryRenameDialog(
|
|
||||||
onDismissRequest = onDismissRequest,
|
|
||||||
onRename = { presenter.renameCategory(dialog.category, it) },
|
|
||||||
category = dialog.category,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
is Dialog.Delete -> {
|
|
||||||
CategoryDeleteDialog(
|
|
||||||
onDismissRequest = onDismissRequest,
|
|
||||||
onDelete = { presenter.deleteCategory(dialog.category) },
|
|
||||||
category = dialog.category,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
presenter.events.collectLatest { event ->
|
|
||||||
when (event) {
|
|
||||||
is CategoryPresenter.Event.CategoryWithNameAlreadyExists -> {
|
|
||||||
context.toast(R.string.error_category_exists)
|
|
||||||
}
|
|
||||||
is CategoryPresenter.Event.InternalError -> {
|
|
||||||
context.toast(R.string.internal_error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
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() }
|
|
||||||
}
|
|
|
@ -8,19 +8,18 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
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(
|
||||||
state: CategoryState,
|
categories: List<Category>,
|
||||||
lazyListState: LazyListState,
|
lazyListState: LazyListState,
|
||||||
paddingValues: PaddingValues,
|
paddingValues: PaddingValues,
|
||||||
|
onClickRename: (Category) -> Unit,
|
||||||
|
onClickDelete: (Category) -> Unit,
|
||||||
onMoveUp: (Category) -> Unit,
|
onMoveUp: (Category) -> Unit,
|
||||||
onMoveDown: (Category) -> Unit,
|
onMoveDown: (Category) -> Unit,
|
||||||
) {
|
) {
|
||||||
val categories = state.categories
|
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
state = lazyListState,
|
state = lazyListState,
|
||||||
contentPadding = paddingValues,
|
contentPadding = paddingValues,
|
||||||
|
@ -37,8 +36,8 @@ fun CategoryContent(
|
||||||
canMoveDown = index != categories.lastIndex,
|
canMoveDown = index != categories.lastIndex,
|
||||||
onMoveUp = onMoveUp,
|
onMoveUp = onMoveUp,
|
||||||
onMoveDown = onMoveDown,
|
onMoveDown = onMoveDown,
|
||||||
onRename = { state.dialog = Dialog.Rename(category) },
|
onRename = { onClickRename(category) },
|
||||||
onDelete = { state.dialog = Dialog.Delete(category) },
|
onDelete = { onClickDelete(category) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,17 @@
|
||||||
package eu.kanade.tachiyomi.ui.category
|
package eu.kanade.tachiyomi.ui.category
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import eu.kanade.presentation.category.CategoryScreen
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.FullComposeController
|
import cafe.adriel.voyager.navigator.Navigator
|
||||||
|
import eu.kanade.presentation.util.LocalRouter
|
||||||
|
import eu.kanade.tachiyomi.ui.base.controller.BasicFullComposeController
|
||||||
|
|
||||||
class CategoryController : FullComposeController<CategoryPresenter>() {
|
class CategoryController : BasicFullComposeController() {
|
||||||
|
|
||||||
override fun createPresenter() = CategoryPresenter()
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun ComposeContent() {
|
override fun ComposeContent() {
|
||||||
CategoryScreen(
|
CompositionLocalProvider(LocalRouter provides router) {
|
||||||
presenter = presenter,
|
Navigator(screen = CategoryScreen())
|
||||||
navigateUp = router::popCurrentController,
|
}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,100 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.ui.category
|
|
||||||
|
|
||||||
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>(), 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.filterNot(Category::isSystemCategory)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun createCategory(name: String) {
|
|
||||||
presenterScope.launchIO {
|
|
||||||
when (createCategoryWithName.await(name)) {
|
|
||||||
is CreateCategoryWithName.Result.NameAlreadyExistsError -> _events.send(Event.CategoryWithNameAlreadyExists)
|
|
||||||
is CreateCategoryWithName.Result.InternalError -> _events.send(Event.InternalError)
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun deleteCategory(category: Category) {
|
|
||||||
presenterScope.launchIO {
|
|
||||||
when (deleteCategory.await(category.id)) {
|
|
||||||
is DeleteCategory.Result.InternalError -> _events.send(Event.InternalError)
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun moveUp(category: Category) {
|
|
||||||
presenterScope.launchIO {
|
|
||||||
when (reorderCategory.await(category, category.order - 1)) {
|
|
||||||
is ReorderCategory.Result.InternalError -> _events.send(Event.InternalError)
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun moveDown(category: Category) {
|
|
||||||
presenterScope.launchIO {
|
|
||||||
when (reorderCategory.await(category, category.order + 1)) {
|
|
||||||
is ReorderCategory.Result.InternalError -> _events.send(Event.InternalError)
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun renameCategory(category: Category, name: String) {
|
|
||||||
presenterScope.launchIO {
|
|
||||||
when (renameCategory.await(category, name)) {
|
|
||||||
RenameCategory.Result.NameAlreadyExistsError -> _events.send(Event.CategoryWithNameAlreadyExists)
|
|
||||||
is RenameCategory.Result.InternalError -> _events.send(Event.InternalError)
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed class Dialog {
|
|
||||||
object Create : Dialog()
|
|
||||||
data class Rename(val category: Category) : Dialog()
|
|
||||||
data class Delete(val category: Category) : Dialog()
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed class Event {
|
|
||||||
object CategoryWithNameAlreadyExists : Event()
|
|
||||||
object InternalError : Event()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
package eu.kanade.tachiyomi.ui.category
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||||
|
import cafe.adriel.voyager.core.screen.Screen
|
||||||
|
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||||
|
import eu.kanade.presentation.category.CategoryScreen
|
||||||
|
import eu.kanade.presentation.category.components.CategoryCreateDialog
|
||||||
|
import eu.kanade.presentation.category.components.CategoryDeleteDialog
|
||||||
|
import eu.kanade.presentation.category.components.CategoryRenameDialog
|
||||||
|
import eu.kanade.presentation.components.LoadingScreen
|
||||||
|
import eu.kanade.presentation.util.LocalRouter
|
||||||
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
|
||||||
|
class CategoryScreen : Screen {
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
override fun Content() {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val router = LocalRouter.currentOrThrow
|
||||||
|
val screenModel = rememberScreenModel { CategoryScreenModel() }
|
||||||
|
|
||||||
|
val state by screenModel.state.collectAsState()
|
||||||
|
|
||||||
|
if (state is CategoryScreenState.Loading) {
|
||||||
|
LoadingScreen()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val successState = state as CategoryScreenState.Success
|
||||||
|
|
||||||
|
CategoryScreen(
|
||||||
|
state = successState,
|
||||||
|
onClickCreate = { screenModel.showDialog(CategoryDialog.Create) },
|
||||||
|
onClickRename = { screenModel.showDialog(CategoryDialog.Rename(it)) },
|
||||||
|
onClickDelete = { screenModel.showDialog(CategoryDialog.Delete(it)) },
|
||||||
|
onClickMoveUp = screenModel::moveUp,
|
||||||
|
onClickMoveDown = screenModel::moveDown,
|
||||||
|
navigateUp = router::popCurrentController,
|
||||||
|
)
|
||||||
|
|
||||||
|
when (val dialog = successState.dialog) {
|
||||||
|
null -> {}
|
||||||
|
CategoryDialog.Create -> {
|
||||||
|
CategoryCreateDialog(
|
||||||
|
onDismissRequest = screenModel::dismissDialog,
|
||||||
|
onCreate = { screenModel.createCategory(it) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is CategoryDialog.Rename -> {
|
||||||
|
CategoryRenameDialog(
|
||||||
|
onDismissRequest = screenModel::dismissDialog,
|
||||||
|
onRename = { screenModel.renameCategory(dialog.category, it) },
|
||||||
|
category = dialog.category,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is CategoryDialog.Delete -> {
|
||||||
|
CategoryDeleteDialog(
|
||||||
|
onDismissRequest = screenModel::dismissDialog,
|
||||||
|
onDelete = { screenModel.deleteCategory(dialog.category.id) },
|
||||||
|
category = dialog.category,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
screenModel.events.collectLatest { event ->
|
||||||
|
if (event is CategoryEvent.LocalizedMessage) {
|
||||||
|
context.toast(event.stringRes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,140 @@
|
||||||
|
package eu.kanade.tachiyomi.ui.category
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import cafe.adriel.voyager.core.model.StateScreenModel
|
||||||
|
import cafe.adriel.voyager.core.model.coroutineScope
|
||||||
|
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.tachiyomi.R
|
||||||
|
import kotlinx.coroutines.channels.Channel
|
||||||
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
import kotlinx.coroutines.flow.consumeAsFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
|
class CategoryScreenModel(
|
||||||
|
private val getCategories: GetCategories = Injekt.get(),
|
||||||
|
private val createCategoryWithName: CreateCategoryWithName = Injekt.get(),
|
||||||
|
private val deleteCategory: DeleteCategory = Injekt.get(),
|
||||||
|
private val reorderCategory: ReorderCategory = Injekt.get(),
|
||||||
|
private val renameCategory: RenameCategory = Injekt.get(),
|
||||||
|
) : StateScreenModel<CategoryScreenState>(CategoryScreenState.Loading) {
|
||||||
|
|
||||||
|
private val _events: Channel<CategoryEvent> = Channel()
|
||||||
|
val events = _events.consumeAsFlow()
|
||||||
|
|
||||||
|
init {
|
||||||
|
coroutineScope.launch {
|
||||||
|
getCategories.subscribe()
|
||||||
|
.collectLatest { categories ->
|
||||||
|
mutableState.update {
|
||||||
|
CategoryScreenState.Success(
|
||||||
|
categories = categories.filterNot(Category::isSystemCategory),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createCategory(name: String) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
when (createCategoryWithName.await(name)) {
|
||||||
|
is CreateCategoryWithName.Result.InternalError -> _events.send(CategoryEvent.InternalError)
|
||||||
|
CreateCategoryWithName.Result.NameAlreadyExistsError -> _events.send(CategoryEvent.CategoryWithNameAlreadyExists)
|
||||||
|
CreateCategoryWithName.Result.Success -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteCategory(categoryId: Long) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
when (deleteCategory.await(categoryId = categoryId)) {
|
||||||
|
is DeleteCategory.Result.InternalError -> _events.send(CategoryEvent.InternalError)
|
||||||
|
DeleteCategory.Result.Success -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun moveUp(category: Category) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
when (reorderCategory.await(category, category.order - 1)) {
|
||||||
|
is ReorderCategory.Result.InternalError -> _events.send(CategoryEvent.InternalError)
|
||||||
|
ReorderCategory.Result.Success -> {}
|
||||||
|
ReorderCategory.Result.Unchanged -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun moveDown(category: Category) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
when (reorderCategory.await(category, category.order + 1)) {
|
||||||
|
is ReorderCategory.Result.InternalError -> _events.send(CategoryEvent.InternalError)
|
||||||
|
ReorderCategory.Result.Success -> {}
|
||||||
|
ReorderCategory.Result.Unchanged -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun renameCategory(category: Category, name: String) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
when (renameCategory.await(category, name)) {
|
||||||
|
is RenameCategory.Result.InternalError -> _events.send(CategoryEvent.InternalError)
|
||||||
|
RenameCategory.Result.NameAlreadyExistsError -> _events.send(CategoryEvent.CategoryWithNameAlreadyExists)
|
||||||
|
RenameCategory.Result.Success -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showDialog(dialog: CategoryDialog) {
|
||||||
|
mutableState.update {
|
||||||
|
when (it) {
|
||||||
|
CategoryScreenState.Loading -> it
|
||||||
|
is CategoryScreenState.Success -> it.copy(dialog = dialog)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dismissDialog() {
|
||||||
|
mutableState.update {
|
||||||
|
when (it) {
|
||||||
|
CategoryScreenState.Loading -> it
|
||||||
|
is CategoryScreenState.Success -> it.copy(dialog = null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class CategoryDialog {
|
||||||
|
object Create : CategoryDialog()
|
||||||
|
data class Rename(val category: Category) : CategoryDialog()
|
||||||
|
data class Delete(val category: Category) : CategoryDialog()
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class CategoryEvent {
|
||||||
|
sealed class LocalizedMessage(@StringRes val stringRes: Int) : CategoryEvent()
|
||||||
|
object CategoryWithNameAlreadyExists : LocalizedMessage(R.string.error_category_exists)
|
||||||
|
object InternalError : LocalizedMessage(R.string.internal_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class CategoryScreenState {
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
object Loading : CategoryScreenState()
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
data class Success(
|
||||||
|
val categories: List<Category>,
|
||||||
|
val dialog: CategoryDialog? = null,
|
||||||
|
) : CategoryScreenState() {
|
||||||
|
|
||||||
|
val isEmpty: Boolean
|
||||||
|
get() = categories.isEmpty()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue