Add Stable interface for Category state (#7539)

(cherry picked from commit a21aa8125e63b9e0d4377aa9c97d6614ee4acf7f)
This commit is contained in:
Andreas 2022-07-15 23:35:19 +02:00 committed by Jobobby04
parent 8a3c9255e5
commit 41855c34de
20 changed files with 288 additions and 118 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
@ -20,6 +19,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
@ -52,25 +52,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

@ -7,8 +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.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
@ -19,6 +17,7 @@ import eu.kanade.presentation.category.components.CategoryFloatingActionButton
import eu.kanade.presentation.category.components.CategoryTopAppBar import eu.kanade.presentation.category.components.CategoryTopAppBar
import eu.kanade.presentation.category.components.genre.SortTagContent import eu.kanade.presentation.category.components.genre.SortTagContent
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
@ -56,19 +55,20 @@ fun SortTagScreen(
}, },
) { paddingValues -> ) { paddingValues ->
val context = LocalContext.current val context = LocalContext.current
val tags by presenter.tags.collectAsState(initial = emptyList()) when {
if (tags.isEmpty()) { presenter.isLoading -> LoadingScreen()
EmptyScreen(textResource = R.string.information_empty_tags) presenter.isEmpty -> EmptyScreen(textResource = R.string.information_empty_category)
} else { else -> {
SortTagContent( SortTagContent(
categories = tags, state = presenter,
lazyListState = lazyListState, lazyListState = lazyListState,
paddingValues = paddingValues + topPaddingValues + PaddingValues(horizontal = horizontalPadding), paddingValues = paddingValues + topPaddingValues + PaddingValues(horizontal = horizontalPadding),
onMoveUp = { tag, index -> presenter.moveUp(tag, index) }, onMoveUp = { tag, index -> presenter.moveUp(tag, index) },
onMoveDown = { tag, index -> presenter.moveDown(tag, index) }, onMoveDown = { tag, index -> presenter.moveDown(tag, index) },
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,27 @@
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.tachiyomi.ui.category.genre.SortTagPresenter
@Stable
interface SortTagState {
val isLoading: Boolean
var dialog: SortTagPresenter.Dialog?
val tags: List<String>
val isEmpty: Boolean
}
fun SortTagState(): SortTagState {
return SortTagStateImpl()
}
class SortTagStateImpl : SortTagState {
override var isLoading: Boolean by mutableStateOf(true)
override var dialog: SortTagPresenter.Dialog? by mutableStateOf(null)
override var tags: List<String> by mutableStateOf(emptyList())
override val isEmpty: Boolean by derivedStateOf { tags.isEmpty() }
}

View File

@ -7,8 +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.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
@ -20,6 +18,7 @@ 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.category.components.sources.SourceCategoryContent import eu.kanade.presentation.category.components.sources.SourceCategoryContent
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
@ -57,18 +56,18 @@ fun SourceCategoryScreen(
}, },
) { 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 -> {
SourceCategoryContent( SourceCategoryContent(
categories = categories, state = presenter,
lazyListState = lazyListState, lazyListState = lazyListState,
paddingValues = paddingValues + topPaddingValues + PaddingValues(horizontal = horizontalPadding), paddingValues = paddingValues + topPaddingValues + PaddingValues(horizontal = horizontalPadding),
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,27 @@
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.tachiyomi.ui.category.sources.SourceCategoryPresenter
@Stable
interface SourceCategoryState {
val isLoading: Boolean
var dialog: SourceCategoryPresenter.Dialog?
val categories: List<String>
val isEmpty: Boolean
}
fun SourceCategoryState(): SourceCategoryState {
return SourceCategoryStateImpl()
}
class SourceCategoryStateImpl : SourceCategoryState {
override var isLoading: Boolean by mutableStateOf(true)
override var dialog: SourceCategoryPresenter.Dialog? by mutableStateOf(null)
override var categories: List<String> by mutableStateOf(emptyList())
override val isEmpty: Boolean by derivedStateOf { categories.isEmpty() }
}

View File

@ -7,8 +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.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
@ -19,6 +17,7 @@ import eu.kanade.presentation.category.components.CategoryFloatingActionButton
import eu.kanade.presentation.category.components.CategoryTopAppBar import eu.kanade.presentation.category.components.CategoryTopAppBar
import eu.kanade.presentation.category.components.repo.SourceRepoContent import eu.kanade.presentation.category.components.repo.SourceRepoContent
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
@ -56,17 +55,18 @@ fun SourceRepoScreen(
}, },
) { paddingValues -> ) { paddingValues ->
val context = LocalContext.current val context = LocalContext.current
val repos by presenter.repos.collectAsState(initial = emptyList()) when {
if (repos.isEmpty()) { presenter.isLoading -> LoadingScreen()
EmptyScreen(textResource = R.string.information_empty_repos) presenter.isEmpty -> EmptyScreen(textResource = R.string.information_empty_category)
} else { else -> {
SourceRepoContent( SourceRepoContent(
repos = repos, state = presenter,
lazyListState = lazyListState, lazyListState = lazyListState,
paddingValues = paddingValues + topPaddingValues + PaddingValues(horizontal = horizontalPadding), paddingValues = paddingValues + topPaddingValues + PaddingValues(horizontal = horizontalPadding),
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,27 @@
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.tachiyomi.ui.category.repos.RepoPresenter
@Stable
interface SourceRepoState {
val isLoading: Boolean
var dialog: RepoPresenter.Dialog?
val repos: List<String>
val isEmpty: Boolean
}
fun SourceRepoState(): SourceRepoState {
return SourceRepoStateImpl()
}
class SourceRepoStateImpl : SourceRepoState {
override var isLoading: Boolean by mutableStateOf(true)
override var dialog: RepoPresenter.Dialog? by mutableStateOf(null)
override var repos: List<String> by mutableStateOf(emptyList())
override val isEmpty: Boolean by derivedStateOf { repos.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(
modifier = modifier,
) { ) {
ElevatedCard {
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

@ -5,32 +5,36 @@ 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.presentation.category.SortTagState
import eu.kanade.presentation.components.LazyColumn import eu.kanade.presentation.components.LazyColumn
import eu.kanade.tachiyomi.ui.category.genre.SortTagPresenter
@Composable @Composable
fun SortTagContent( fun SortTagContent(
categories: List<String>, state: SortTagState,
lazyListState: LazyListState, lazyListState: LazyListState,
paddingValues: PaddingValues, paddingValues: PaddingValues,
onMoveUp: (String, Int) -> Unit, onMoveUp: (String, Int) -> Unit,
onMoveDown: (String, Int) -> Unit, onMoveDown: (String, Int) -> Unit,
onDelete: (String) -> Unit,
) { ) {
val tags = state.tags
LazyColumn( LazyColumn(
state = lazyListState, state = lazyListState,
contentPadding = paddingValues, contentPadding = paddingValues,
verticalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp),
) { ) {
itemsIndexed(categories) { index, tag -> itemsIndexed(tags) { index, tag ->
SortTagListItem( SortTagListItem(
modifier = Modifier.animateItemPlacement(),
tag = tag, tag = tag,
index = index, index = index,
canMoveUp = index != 0, canMoveUp = index != 0,
canMoveDown = index != categories.lastIndex, canMoveDown = index != tags.lastIndex,
onMoveUp = onMoveUp, onMoveUp = onMoveUp,
onMoveDown = onMoveDown, onMoveDown = onMoveDown,
onDelete = onDelete, onDelete = { state.dialog = SortTagPresenter.Dialog.Delete(it) },
) )
} }
} }

View File

@ -19,6 +19,7 @@ import eu.kanade.presentation.util.horizontalPadding
@Composable @Composable
fun SortTagListItem( fun SortTagListItem(
modifier: Modifier,
tag: String, tag: String,
index: Int, index: Int,
canMoveUp: Boolean, canMoveUp: Boolean,
@ -27,7 +28,9 @@ fun SortTagListItem(
onMoveDown: (String, Int) -> Unit, onMoveDown: (String, Int) -> Unit,
onDelete: (String) -> Unit, onDelete: (String) -> 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),

View File

@ -5,16 +5,19 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
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.presentation.category.SourceRepoState
import eu.kanade.presentation.components.LazyColumn import eu.kanade.presentation.components.LazyColumn
import eu.kanade.tachiyomi.ui.category.repos.RepoPresenter
@Composable @Composable
fun SourceRepoContent( fun SourceRepoContent(
repos: List<String>, state: SourceRepoState,
lazyListState: LazyListState, lazyListState: LazyListState,
paddingValues: PaddingValues, paddingValues: PaddingValues,
onDelete: (String) -> Unit,
) { ) {
val repos = state.repos
LazyColumn( LazyColumn(
state = lazyListState, state = lazyListState,
contentPadding = paddingValues, contentPadding = paddingValues,
@ -22,8 +25,9 @@ fun SourceRepoContent(
) { ) {
items(repos) { repo -> items(repos) { repo ->
SourceRepoListItem( SourceRepoListItem(
modifier = Modifier.animateItemPlacement(),
repo = repo, repo = repo,
onDelete = onDelete, onDelete = { state.dialog = RepoPresenter.Dialog.Delete(it) },
) )
} }
} }

View File

@ -17,10 +17,13 @@ import eu.kanade.presentation.util.horizontalPadding
@Composable @Composable
fun SourceRepoListItem( fun SourceRepoListItem(
modifier: Modifier,
repo: String, repo: String,
onDelete: (String) -> Unit, onDelete: (String) -> 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),

View File

@ -3,29 +3,32 @@ package eu.kanade.presentation.category.components.sources
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues 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.items
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.presentation.category.SourceCategoryState
import eu.kanade.presentation.components.LazyColumn import eu.kanade.presentation.components.LazyColumn
import eu.kanade.tachiyomi.ui.category.sources.SourceCategoryPresenter
@Composable @Composable
fun SourceCategoryContent( fun SourceCategoryContent(
categories: List<String>, state: SourceCategoryState,
lazyListState: LazyListState, lazyListState: LazyListState,
paddingValues: PaddingValues, paddingValues: PaddingValues,
onRename: (String) -> Unit,
onDelete: (String) -> 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 -> items(categories) { category ->
SourceCategoryListItem( SourceCategoryListItem(
modifier = Modifier.animateItemPlacement(),
category = category, category = category,
onRename = onRename, onRename = { state.dialog = SourceCategoryPresenter.Dialog.Rename(category) },
onDelete = onDelete, onDelete = { state.dialog = SourceCategoryPresenter.Dialog.Delete(category) },
) )
} }
} }

View File

@ -18,11 +18,14 @@ import eu.kanade.presentation.util.horizontalPadding
@Composable @Composable
fun SourceCategoryListItem( fun SourceCategoryListItem(
modifier: Modifier,
category: String, category: String,
onRename: (String) -> Unit, onRename: (String) -> Unit,
onDelete: (String) -> Unit, onDelete: (String) -> 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),

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)) {

View File

@ -1,15 +1,16 @@
package eu.kanade.tachiyomi.ui.category.genre package eu.kanade.tachiyomi.ui.category.genre
import androidx.compose.runtime.getValue import android.os.Bundle
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import eu.kanade.domain.manga.interactor.CreateSortTag import eu.kanade.domain.manga.interactor.CreateSortTag
import eu.kanade.domain.manga.interactor.DeleteSortTag import eu.kanade.domain.manga.interactor.DeleteSortTag
import eu.kanade.domain.manga.interactor.GetSortTag import eu.kanade.domain.manga.interactor.GetSortTag
import eu.kanade.domain.manga.interactor.ReorderSortTag import eu.kanade.domain.manga.interactor.ReorderSortTag
import eu.kanade.presentation.category.SortTagState
import eu.kanade.presentation.category.SortTagStateImpl
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
@ -18,22 +19,27 @@ import uy.kohesive.injekt.api.get
* Presenter of [SortTagController]. Used to manage the categories of the library. * Presenter of [SortTagController]. Used to manage the categories of the library.
*/ */
class SortTagPresenter( class SortTagPresenter(
private val state: SortTagStateImpl = SortTagState() as SortTagStateImpl,
private val getSortTag: GetSortTag = Injekt.get(), private val getSortTag: GetSortTag = Injekt.get(),
private val createSortTag: CreateSortTag = Injekt.get(), private val createSortTag: CreateSortTag = Injekt.get(),
private val deleteSortTag: DeleteSortTag = Injekt.get(), private val deleteSortTag: DeleteSortTag = Injekt.get(),
private val reorderSortTag: ReorderSortTag = Injekt.get(), private val reorderSortTag: ReorderSortTag = Injekt.get(),
) : BasePresenter<SortTagController>() { ) : BasePresenter<SortTagController>(), SortTagState by state {
var dialog: Dialog? by mutableStateOf(null)
/**
* List containing categories.
*/
val tags = getSortTag.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 {
getSortTag.subscribe()
.collectLatest {
state.isLoading = false
state.tags = it
}
}
}
fun createTag(name: String) { fun createTag(name: String) {
presenterScope.launchIO { presenterScope.launchIO {
when (createSortTag.await(name)) { when (createSortTag.await(name)) {

View File

@ -1,14 +1,15 @@
package eu.kanade.tachiyomi.ui.category.repos package eu.kanade.tachiyomi.ui.category.repos
import androidx.compose.runtime.getValue import android.os.Bundle
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import eu.kanade.domain.source.interactor.CreateSourceRepo import eu.kanade.domain.source.interactor.CreateSourceRepo
import eu.kanade.domain.source.interactor.DeleteSourceRepos import eu.kanade.domain.source.interactor.DeleteSourceRepos
import eu.kanade.domain.source.interactor.GetSourceRepos import eu.kanade.domain.source.interactor.GetSourceRepos
import eu.kanade.presentation.category.SourceRepoState
import eu.kanade.presentation.category.SourceRepoStateImpl
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
@ -17,18 +18,26 @@ import uy.kohesive.injekt.api.get
* Presenter of [RepoController]. Used to manage the repos for the extensions. * Presenter of [RepoController]. Used to manage the repos for the extensions.
*/ */
class RepoPresenter( class RepoPresenter(
private val state: SourceRepoStateImpl = SourceRepoState() as SourceRepoStateImpl,
private val getSourceRepos: GetSourceRepos = Injekt.get(), private val getSourceRepos: GetSourceRepos = Injekt.get(),
private val createSourceRepo: CreateSourceRepo = Injekt.get(), private val createSourceRepo: CreateSourceRepo = Injekt.get(),
private val deleteSourceRepos: DeleteSourceRepos = Injekt.get(), private val deleteSourceRepos: DeleteSourceRepos = Injekt.get(),
) : BasePresenter<RepoController>() { ) : BasePresenter<RepoController>(), SourceRepoState by state {
var dialog: Dialog? by mutableStateOf(null)
val repos = getSourceRepos.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 {
getSourceRepos.subscribe()
.collectLatest {
state.isLoading = false
state.repos = it
}
}
}
/** /**
* Creates and adds a new repo to the database. * Creates and adds a new repo to the database.
* *

View File

@ -1,15 +1,16 @@
package eu.kanade.tachiyomi.ui.category.sources package eu.kanade.tachiyomi.ui.category.sources
import androidx.compose.runtime.getValue import android.os.Bundle
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import eu.kanade.domain.source.interactor.CreateSourceCategory import eu.kanade.domain.source.interactor.CreateSourceCategory
import eu.kanade.domain.source.interactor.DeleteSourceCategory import eu.kanade.domain.source.interactor.DeleteSourceCategory
import eu.kanade.domain.source.interactor.GetSourceCategories import eu.kanade.domain.source.interactor.GetSourceCategories
import eu.kanade.domain.source.interactor.RenameSourceCategory import eu.kanade.domain.source.interactor.RenameSourceCategory
import eu.kanade.presentation.category.SourceCategoryState
import eu.kanade.presentation.category.SourceCategoryStateImpl
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
@ -18,19 +19,27 @@ import uy.kohesive.injekt.api.get
* Presenter of [SourceCategoryController]. Used to manage the categories of the library. * Presenter of [SourceCategoryController]. Used to manage the categories of the library.
*/ */
class SourceCategoryPresenter( class SourceCategoryPresenter(
private val state: SourceCategoryStateImpl = SourceCategoryState() as SourceCategoryStateImpl,
private val getSourceCategories: GetSourceCategories = Injekt.get(), private val getSourceCategories: GetSourceCategories = Injekt.get(),
private val createSourceCategory: CreateSourceCategory = Injekt.get(), private val createSourceCategory: CreateSourceCategory = Injekt.get(),
private val renameSourceCategory: RenameSourceCategory = Injekt.get(), private val renameSourceCategory: RenameSourceCategory = Injekt.get(),
private val deleteSourceCategory: DeleteSourceCategory = Injekt.get(), private val deleteSourceCategory: DeleteSourceCategory = Injekt.get(),
) : BasePresenter<SourceCategoryController>() { ) : BasePresenter<SourceCategoryController>(), SourceCategoryState by state {
var dialog: Dialog? by mutableStateOf(null)
val categories = getSourceCategories.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 {
getSourceCategories.subscribe()
.collectLatest {
state.isLoading = false
state.categories = it
}
}
}
/** /**
* Creates and adds a new category to the database. * Creates and adds a new category to the database.
* *