Use Compose for Category screen (#7454)
* Use Compose for Category screen
* Use correct string for CategoryRenameDialog title
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 86bacbe586)
# Conflicts:
# app/src/main/java/eu/kanade/data/category/CategoryRepositoryImpl.kt
# app/src/main/java/eu/kanade/domain/category/interactor/InsertCategory.kt
# app/src/main/java/eu/kanade/domain/category/repository/CategoryRepository.kt
# app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryAdapter.kt
# app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryHolder.kt
# app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryItem.kt
# app/src/main/res/layout/categories_item.xml
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
package eu.kanade.presentation.category
|
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
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
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
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.CategoryRenameDialog
|
||||
import eu.kanade.presentation.category.components.CategoryTopAppBar
|
||||
import eu.kanade.presentation.components.EmptyScreen
|
||||
import eu.kanade.presentation.components.Scaffold
|
||||
import eu.kanade.presentation.util.horizontalPadding
|
||||
import eu.kanade.presentation.util.plus
|
||||
import eu.kanade.presentation.util.topPaddingValues
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.category.CategoryPresenter
|
||||
import eu.kanade.tachiyomi.ui.category.CategoryPresenter.Dialog
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
|
||||
@Composable
|
||||
fun CategoryScreen(
|
||||
presenter: CategoryPresenter,
|
||||
navigateUp: () -> Unit,
|
||||
) {
|
||||
val lazyListState = rememberLazyListState()
|
||||
val topAppBarScrollState = rememberTopAppBarScrollState()
|
||||
val topAppBarScrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(topAppBarScrollState)
|
||||
Scaffold(
|
||||
modifier = Modifier
|
||||
.statusBarsPadding()
|
||||
.nestedScroll(topAppBarScrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
CategoryTopAppBar(
|
||||
topAppBarScrollBehavior = topAppBarScrollBehavior,
|
||||
navigateUp = navigateUp,
|
||||
title = stringResource(id = R.string.action_edit_categories),
|
||||
)
|
||||
},
|
||||
floatingActionButton = {
|
||||
CategoryFloatingActionButton(
|
||||
lazyListState = lazyListState,
|
||||
onCreate = { presenter.dialog = CategoryPresenter.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) },
|
||||
)
|
||||
}
|
||||
val onDismissRequest = { presenter.dialog = null }
|
||||
when (val dialog = presenter.dialog) {
|
||||
Dialog.Create -> {
|
||||
CategoryCreateDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
onCreate = { presenter.createCategory(it) },
|
||||
title = stringResource(R.string.action_add_category),
|
||||
)
|
||||
}
|
||||
is Dialog.Rename -> {
|
||||
CategoryRenameDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
onRename = { presenter.renameCategory(dialog.category, it) },
|
||||
category = dialog.category.name,
|
||||
)
|
||||
}
|
||||
is Dialog.Delete -> {
|
||||
CategoryDeleteDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
onDelete = { presenter.deleteCategory(dialog.category) },
|
||||
title = stringResource(R.string.delete_category),
|
||||
text = stringResource(R.string.delete_category_confirmation, dialog.category.name),
|
||||
)
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package eu.kanade.presentation.category
|
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
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
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
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.CategoryTopAppBar
|
||||
import eu.kanade.presentation.category.components.genre.SortTagContent
|
||||
import eu.kanade.presentation.components.EmptyScreen
|
||||
import eu.kanade.presentation.components.Scaffold
|
||||
import eu.kanade.presentation.util.horizontalPadding
|
||||
import eu.kanade.presentation.util.plus
|
||||
import eu.kanade.presentation.util.topPaddingValues
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.category.genre.SortTagPresenter
|
||||
import eu.kanade.tachiyomi.ui.category.genre.SortTagPresenter.Dialog
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
|
||||
@Composable
|
||||
fun SortTagScreen(
|
||||
presenter: SortTagPresenter,
|
||||
navigateUp: () -> Unit,
|
||||
) {
|
||||
val lazyListState = rememberLazyListState()
|
||||
val topAppBarScrollState = rememberTopAppBarScrollState()
|
||||
val topAppBarScrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(topAppBarScrollState)
|
||||
Scaffold(
|
||||
modifier = Modifier
|
||||
.statusBarsPadding()
|
||||
.nestedScroll(topAppBarScrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
CategoryTopAppBar(
|
||||
topAppBarScrollBehavior = topAppBarScrollBehavior,
|
||||
navigateUp = navigateUp,
|
||||
title = stringResource(id = R.string.action_edit_tags),
|
||||
)
|
||||
},
|
||||
floatingActionButton = {
|
||||
CategoryFloatingActionButton(
|
||||
lazyListState = lazyListState,
|
||||
onCreate = { presenter.dialog = Dialog.Create },
|
||||
)
|
||||
},
|
||||
) { paddingValues ->
|
||||
val context = LocalContext.current
|
||||
val tags by presenter.tags.collectAsState(initial = emptyList())
|
||||
if (tags.isEmpty()) {
|
||||
EmptyScreen(textResource = R.string.information_empty_tags)
|
||||
} else {
|
||||
SortTagContent(
|
||||
categories = tags,
|
||||
lazyListState = lazyListState,
|
||||
paddingValues = paddingValues + topPaddingValues + PaddingValues(horizontal = horizontalPadding),
|
||||
onMoveUp = { tag, index -> presenter.moveUp(tag, index) },
|
||||
onMoveDown = { tag, index -> presenter.moveDown(tag, index) },
|
||||
onDelete = { presenter.dialog = Dialog.Delete(it) },
|
||||
)
|
||||
}
|
||||
val onDismissRequest = { presenter.dialog = null }
|
||||
when (val dialog = presenter.dialog) {
|
||||
Dialog.Create -> {
|
||||
CategoryCreateDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
onCreate = { presenter.createTag(it) },
|
||||
title = stringResource(R.string.add_tag),
|
||||
extraMessage = stringResource(R.string.action_add_tags_message),
|
||||
)
|
||||
}
|
||||
is Dialog.Delete -> {
|
||||
CategoryDeleteDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
onDelete = { presenter.delete(dialog.tag) },
|
||||
title = stringResource(R.string.delete_tag),
|
||||
text = stringResource(R.string.delete_tag_confirmation, dialog.tag),
|
||||
)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
presenter.events.collectLatest { event ->
|
||||
when (event) {
|
||||
is SortTagPresenter.Event.TagExists -> {
|
||||
context.toast(R.string.error_tag_exists)
|
||||
}
|
||||
is SortTagPresenter.Event.InternalError -> {
|
||||
context.toast(R.string.internal_error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package eu.kanade.presentation.category
|
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
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
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
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.CategoryRenameDialog
|
||||
import eu.kanade.presentation.category.components.CategoryTopAppBar
|
||||
import eu.kanade.presentation.category.components.sources.SourceCategoryContent
|
||||
import eu.kanade.presentation.components.EmptyScreen
|
||||
import eu.kanade.presentation.components.Scaffold
|
||||
import eu.kanade.presentation.util.horizontalPadding
|
||||
import eu.kanade.presentation.util.plus
|
||||
import eu.kanade.presentation.util.topPaddingValues
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.category.sources.SourceCategoryPresenter
|
||||
import eu.kanade.tachiyomi.ui.category.sources.SourceCategoryPresenter.Dialog
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
|
||||
@Composable
|
||||
fun SourceCategoryScreen(
|
||||
presenter: SourceCategoryPresenter,
|
||||
navigateUp: () -> Unit,
|
||||
) {
|
||||
val lazyListState = rememberLazyListState()
|
||||
val topAppBarScrollState = rememberTopAppBarScrollState()
|
||||
val topAppBarScrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(topAppBarScrollState)
|
||||
Scaffold(
|
||||
modifier = Modifier
|
||||
.statusBarsPadding()
|
||||
.nestedScroll(topAppBarScrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
CategoryTopAppBar(
|
||||
topAppBarScrollBehavior = topAppBarScrollBehavior,
|
||||
navigateUp = navigateUp,
|
||||
title = stringResource(id = R.string.action_edit_categories),
|
||||
)
|
||||
},
|
||||
floatingActionButton = {
|
||||
CategoryFloatingActionButton(
|
||||
lazyListState = lazyListState,
|
||||
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 {
|
||||
SourceCategoryContent(
|
||||
categories = categories,
|
||||
lazyListState = lazyListState,
|
||||
paddingValues = paddingValues + topPaddingValues + PaddingValues(horizontal = horizontalPadding),
|
||||
onRename = { presenter.dialog = Dialog.Rename(it) },
|
||||
onDelete = { presenter.dialog = Dialog.Delete(it) },
|
||||
)
|
||||
}
|
||||
val onDismissRequest = { presenter.dialog = null }
|
||||
when (val dialog = presenter.dialog) {
|
||||
Dialog.Create -> {
|
||||
CategoryCreateDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
onCreate = { presenter.createCategory(it) },
|
||||
title = stringResource(R.string.action_add_category),
|
||||
)
|
||||
}
|
||||
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) },
|
||||
title = stringResource(R.string.delete_category),
|
||||
text = stringResource(R.string.delete_category_confirmation, dialog.category),
|
||||
)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
presenter.events.collectLatest { event ->
|
||||
when (event) {
|
||||
is SourceCategoryPresenter.Event.CategoryExists -> {
|
||||
context.toast(R.string.error_category_exists)
|
||||
}
|
||||
is SourceCategoryPresenter.Event.InternalError -> {
|
||||
context.toast(R.string.internal_error)
|
||||
}
|
||||
SourceCategoryPresenter.Event.InvalidName -> {
|
||||
context.toast(R.string.invalid_category_name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package eu.kanade.presentation.category
|
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
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
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
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.CategoryTopAppBar
|
||||
import eu.kanade.presentation.category.components.repo.SourceRepoContent
|
||||
import eu.kanade.presentation.components.EmptyScreen
|
||||
import eu.kanade.presentation.components.Scaffold
|
||||
import eu.kanade.presentation.util.horizontalPadding
|
||||
import eu.kanade.presentation.util.plus
|
||||
import eu.kanade.presentation.util.topPaddingValues
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.category.repos.RepoPresenter
|
||||
import eu.kanade.tachiyomi.ui.category.repos.RepoPresenter.Dialog
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
|
||||
@Composable
|
||||
fun SourceRepoScreen(
|
||||
presenter: RepoPresenter,
|
||||
navigateUp: () -> Unit,
|
||||
) {
|
||||
val lazyListState = rememberLazyListState()
|
||||
val topAppBarScrollState = rememberTopAppBarScrollState()
|
||||
val topAppBarScrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(topAppBarScrollState)
|
||||
Scaffold(
|
||||
modifier = Modifier
|
||||
.statusBarsPadding()
|
||||
.nestedScroll(topAppBarScrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
CategoryTopAppBar(
|
||||
topAppBarScrollBehavior = topAppBarScrollBehavior,
|
||||
navigateUp = navigateUp,
|
||||
title = stringResource(R.string.action_edit_repos),
|
||||
)
|
||||
},
|
||||
floatingActionButton = {
|
||||
CategoryFloatingActionButton(
|
||||
lazyListState = lazyListState,
|
||||
onCreate = { presenter.dialog = Dialog.Create },
|
||||
)
|
||||
},
|
||||
) { paddingValues ->
|
||||
val context = LocalContext.current
|
||||
val repos by presenter.repos.collectAsState(initial = emptyList())
|
||||
if (repos.isEmpty()) {
|
||||
EmptyScreen(textResource = R.string.information_empty_repos)
|
||||
} else {
|
||||
SourceRepoContent(
|
||||
repos = repos,
|
||||
lazyListState = lazyListState,
|
||||
paddingValues = paddingValues + topPaddingValues + PaddingValues(horizontal = horizontalPadding),
|
||||
onDelete = { presenter.dialog = Dialog.Delete(it) },
|
||||
)
|
||||
}
|
||||
val onDismissRequest = { presenter.dialog = null }
|
||||
when (val dialog = presenter.dialog) {
|
||||
Dialog.Create -> {
|
||||
CategoryCreateDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
onCreate = { presenter.createRepo(it) },
|
||||
title = stringResource(R.string.action_add_repo),
|
||||
extraMessage = stringResource(R.string.action_add_repo_message),
|
||||
)
|
||||
}
|
||||
is Dialog.Delete -> {
|
||||
CategoryDeleteDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
onDelete = { presenter.deleteRepos(listOf(dialog.repo)) },
|
||||
title = stringResource(R.string.delete_repo),
|
||||
text = stringResource(R.string.delete_repo_confirmation, dialog.repo),
|
||||
)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
presenter.events.collectLatest { event ->
|
||||
when (event) {
|
||||
is RepoPresenter.Event.RepoExists -> {
|
||||
context.toast(R.string.error_repo_exists)
|
||||
}
|
||||
is RepoPresenter.Event.InternalError -> {
|
||||
context.toast(R.string.internal_error)
|
||||
}
|
||||
is RepoPresenter.Event.InvalidName -> {
|
||||
context.toast(R.string.invalid_repo_name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package eu.kanade.presentation.category.components
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.unit.dp
|
||||
import eu.kanade.domain.category.model.Category
|
||||
|
||||
@Composable
|
||||
fun CategoryContent(
|
||||
categories: List<Category>,
|
||||
lazyListState: LazyListState,
|
||||
paddingValues: PaddingValues,
|
||||
onMoveUp: (Category) -> Unit,
|
||||
onMoveDown: (Category) -> Unit,
|
||||
onRename: (Category) -> Unit,
|
||||
onDelete: (Category) -> Unit,
|
||||
) {
|
||||
LazyColumn(
|
||||
state = lazyListState,
|
||||
contentPadding = paddingValues,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
itemsIndexed(categories) { index, category ->
|
||||
CategoryListItem(
|
||||
category = category,
|
||||
canMoveUp = index != 0,
|
||||
canMoveDown = index != categories.lastIndex,
|
||||
onMoveUp = onMoveUp,
|
||||
onMoveDown = onMoveDown,
|
||||
onRename = onRename,
|
||||
onDelete = onDelete,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package eu.kanade.presentation.category.components
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import eu.kanade.presentation.components.TextButton
|
||||
import eu.kanade.tachiyomi.R
|
||||
|
||||
@Composable
|
||||
fun CategoryCreateDialog(
|
||||
onDismissRequest: () -> Unit,
|
||||
onCreate: (String) -> Unit,
|
||||
// SY -->
|
||||
title: String,
|
||||
extraMessage: String? = null,
|
||||
// SY <--
|
||||
) {
|
||||
val (name, onNameChange) = remember { mutableStateOf("") }
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
confirmButton = {
|
||||
TextButton(onClick = {
|
||||
onCreate(name)
|
||||
onDismissRequest()
|
||||
},) {
|
||||
Text(text = stringResource(id = R.string.action_add))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = onDismissRequest) {
|
||||
Text(text = stringResource(id = R.string.action_cancel))
|
||||
}
|
||||
},
|
||||
title = {
|
||||
Text(text = title)
|
||||
},
|
||||
text = {
|
||||
// SY -->
|
||||
Column {
|
||||
if (extraMessage != null) {
|
||||
Text(extraMessage)
|
||||
}
|
||||
// SY <--
|
||||
OutlinedTextField(
|
||||
value = name,
|
||||
onValueChange = onNameChange,
|
||||
label = {
|
||||
Text(text = stringResource(id = R.string.name))
|
||||
},
|
||||
)
|
||||
// SY -->
|
||||
}
|
||||
// SY <--
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CategoryRenameDialog(
|
||||
onDismissRequest: () -> Unit,
|
||||
onRename: (String) -> Unit,
|
||||
category: String,
|
||||
) {
|
||||
val (name, onNameChange) = remember { mutableStateOf(category) }
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
confirmButton = {
|
||||
TextButton(onClick = {
|
||||
onRename(name)
|
||||
onDismissRequest()
|
||||
},) {
|
||||
Text(text = stringResource(id = android.R.string.ok))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = onDismissRequest) {
|
||||
Text(text = stringResource(id = R.string.action_cancel))
|
||||
}
|
||||
},
|
||||
title = {
|
||||
Text(text = stringResource(id = R.string.action_rename_category))
|
||||
},
|
||||
text = {
|
||||
OutlinedTextField(
|
||||
value = name,
|
||||
onValueChange = onNameChange,
|
||||
label = {
|
||||
Text(text = stringResource(id = R.string.name))
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CategoryDeleteDialog(
|
||||
onDismissRequest: () -> Unit,
|
||||
onDelete: () -> Unit,
|
||||
title: String,
|
||||
text: String,
|
||||
) {
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
confirmButton = {
|
||||
TextButton(onClick = onDismissRequest) {
|
||||
Text(text = stringResource(R.string.no))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = {
|
||||
onDelete()
|
||||
onDismissRequest()
|
||||
},) {
|
||||
Text(text = stringResource(R.string.yes))
|
||||
}
|
||||
},
|
||||
title = {
|
||||
Text(text = title)
|
||||
},
|
||||
text = {
|
||||
Text(text = text)
|
||||
},
|
||||
)
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package eu.kanade.presentation.category.components
|
||||
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Add
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import eu.kanade.presentation.components.ExtendedFloatingActionButton
|
||||
import eu.kanade.presentation.util.isScrolledToEnd
|
||||
import eu.kanade.presentation.util.isScrollingUp
|
||||
import eu.kanade.tachiyomi.R
|
||||
|
||||
@Composable
|
||||
fun CategoryFloatingActionButton(
|
||||
lazyListState: LazyListState,
|
||||
onCreate: () -> Unit,
|
||||
) {
|
||||
ExtendedFloatingActionButton(
|
||||
text = { Text(text = stringResource(id = R.string.action_add)) },
|
||||
icon = { Icon(imageVector = Icons.Outlined.Add, contentDescription = "") },
|
||||
onClick = onCreate,
|
||||
modifier = Modifier
|
||||
.navigationBarsPadding(),
|
||||
expanded = lazyListState.isScrollingUp() || lazyListState.isScrolledToEnd(),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package eu.kanade.presentation.category.components
|
||||
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.ArrowDropDown
|
||||
import androidx.compose.material.icons.outlined.ArrowDropUp
|
||||
import androidx.compose.material.icons.outlined.Delete
|
||||
import androidx.compose.material.icons.outlined.Edit
|
||||
import androidx.compose.material.icons.outlined.Label
|
||||
import androidx.compose.material3.ElevatedCard
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import eu.kanade.domain.category.model.Category
|
||||
import eu.kanade.presentation.util.horizontalPadding
|
||||
|
||||
@Composable
|
||||
fun CategoryListItem(
|
||||
category: Category,
|
||||
canMoveUp: Boolean,
|
||||
canMoveDown: Boolean,
|
||||
onMoveUp: (Category) -> Unit,
|
||||
onMoveDown: (Category) -> Unit,
|
||||
onRename: (Category) -> Unit,
|
||||
onDelete: (Category) -> Unit,
|
||||
) {
|
||||
ElevatedCard {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(start = horizontalPadding, top = horizontalPadding, end = horizontalPadding),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(imageVector = Icons.Outlined.Label, contentDescription = "")
|
||||
Text(text = category.name, modifier = Modifier.padding(start = horizontalPadding))
|
||||
}
|
||||
Row {
|
||||
IconButton(
|
||||
onClick = { onMoveUp(category) },
|
||||
enabled = canMoveUp,
|
||||
) {
|
||||
Icon(imageVector = Icons.Outlined.ArrowDropUp, contentDescription = "")
|
||||
}
|
||||
IconButton(
|
||||
onClick = { onMoveDown(category) },
|
||||
enabled = canMoveDown,
|
||||
) {
|
||||
Icon(imageVector = Icons.Outlined.ArrowDropDown, contentDescription = "")
|
||||
}
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
IconButton(onClick = { onRename(category) }) {
|
||||
Icon(imageVector = Icons.Outlined.Edit, contentDescription = "")
|
||||
}
|
||||
IconButton(onClick = { onDelete(category) }) {
|
||||
Icon(imageVector = Icons.Outlined.Delete, contentDescription = "")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package eu.kanade.presentation.category.components
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.SmallTopAppBar
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import eu.kanade.tachiyomi.R
|
||||
|
||||
@Composable
|
||||
fun CategoryTopAppBar(
|
||||
topAppBarScrollBehavior: TopAppBarScrollBehavior,
|
||||
navigateUp: () -> Unit,
|
||||
title: String,
|
||||
) {
|
||||
SmallTopAppBar(
|
||||
navigationIcon = {
|
||||
IconButton(onClick = navigateUp) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowBack,
|
||||
contentDescription = stringResource(R.string.abc_action_bar_up_description),
|
||||
)
|
||||
}
|
||||
},
|
||||
title = {
|
||||
Text(text = title)
|
||||
},
|
||||
scrollBehavior = topAppBarScrollBehavior,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package eu.kanade.presentation.category.components.genre
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun SortTagContent(
|
||||
categories: List<String>,
|
||||
lazyListState: LazyListState,
|
||||
paddingValues: PaddingValues,
|
||||
onMoveUp: (String, Int) -> Unit,
|
||||
onMoveDown: (String, Int) -> Unit,
|
||||
onDelete: (String) -> Unit,
|
||||
) {
|
||||
LazyColumn(
|
||||
state = lazyListState,
|
||||
contentPadding = paddingValues,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
itemsIndexed(categories) { index, tag ->
|
||||
SortTagListItem(
|
||||
tag = tag,
|
||||
index = index,
|
||||
canMoveUp = index != 0,
|
||||
canMoveDown = index != categories.lastIndex,
|
||||
onMoveUp = onMoveUp,
|
||||
onMoveDown = onMoveDown,
|
||||
onDelete = onDelete,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package eu.kanade.presentation.category.components.genre
|
||||
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.ArrowDropDown
|
||||
import androidx.compose.material.icons.outlined.ArrowDropUp
|
||||
import androidx.compose.material.icons.outlined.Delete
|
||||
import androidx.compose.material.icons.outlined.Label
|
||||
import androidx.compose.material3.ElevatedCard
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import eu.kanade.presentation.util.horizontalPadding
|
||||
|
||||
@Composable
|
||||
fun SortTagListItem(
|
||||
tag: String,
|
||||
index: Int,
|
||||
canMoveUp: Boolean,
|
||||
canMoveDown: Boolean,
|
||||
onMoveUp: (String, Int) -> Unit,
|
||||
onMoveDown: (String, Int) -> Unit,
|
||||
onDelete: (String) -> Unit,
|
||||
) {
|
||||
ElevatedCard {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(start = horizontalPadding, top = horizontalPadding, end = horizontalPadding),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(imageVector = Icons.Outlined.Label, contentDescription = "")
|
||||
Text(text = tag, modifier = Modifier.padding(start = horizontalPadding))
|
||||
}
|
||||
Row {
|
||||
IconButton(
|
||||
onClick = { onMoveUp(tag, index) },
|
||||
enabled = canMoveUp,
|
||||
) {
|
||||
Icon(imageVector = Icons.Outlined.ArrowDropUp, contentDescription = "")
|
||||
}
|
||||
IconButton(
|
||||
onClick = { onMoveDown(tag, index) },
|
||||
enabled = canMoveDown,
|
||||
) {
|
||||
Icon(imageVector = Icons.Outlined.ArrowDropDown, contentDescription = "")
|
||||
}
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
IconButton(onClick = { onDelete(tag) }) {
|
||||
Icon(imageVector = Icons.Outlined.Delete, contentDescription = "")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package eu.kanade.presentation.category.components.repo
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun SourceRepoContent(
|
||||
repos: List<String>,
|
||||
lazyListState: LazyListState,
|
||||
paddingValues: PaddingValues,
|
||||
onDelete: (String) -> Unit,
|
||||
) {
|
||||
LazyColumn(
|
||||
state = lazyListState,
|
||||
contentPadding = paddingValues,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
items(repos) { repo ->
|
||||
SourceRepoListItem(
|
||||
repo = repo,
|
||||
onDelete = onDelete,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package eu.kanade.presentation.category.components.repo
|
||||
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Delete
|
||||
import androidx.compose.material.icons.outlined.Label
|
||||
import androidx.compose.material3.ElevatedCard
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import eu.kanade.presentation.util.horizontalPadding
|
||||
|
||||
@Composable
|
||||
fun SourceRepoListItem(
|
||||
repo: String,
|
||||
onDelete: (String) -> Unit,
|
||||
) {
|
||||
ElevatedCard {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(start = horizontalPadding, top = horizontalPadding, end = horizontalPadding),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(imageVector = Icons.Outlined.Label, contentDescription = "")
|
||||
Text(text = repo, modifier = Modifier.padding(start = horizontalPadding))
|
||||
}
|
||||
Row {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
IconButton(onClick = { onDelete(repo) }) {
|
||||
Icon(imageVector = Icons.Outlined.Delete, contentDescription = "")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package eu.kanade.presentation.category.components.sources
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun SourceCategoryContent(
|
||||
categories: List<String>,
|
||||
lazyListState: LazyListState,
|
||||
paddingValues: PaddingValues,
|
||||
onRename: (String) -> Unit,
|
||||
onDelete: (String) -> Unit,
|
||||
) {
|
||||
LazyColumn(
|
||||
state = lazyListState,
|
||||
contentPadding = paddingValues,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
itemsIndexed(categories) { index, category ->
|
||||
SourceCategoryListItem(
|
||||
category = category,
|
||||
onRename = onRename,
|
||||
onDelete = onDelete,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package eu.kanade.presentation.category.components.sources
|
||||
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Delete
|
||||
import androidx.compose.material.icons.outlined.Edit
|
||||
import androidx.compose.material.icons.outlined.Label
|
||||
import androidx.compose.material3.ElevatedCard
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import eu.kanade.presentation.util.horizontalPadding
|
||||
|
||||
@Composable
|
||||
fun SourceCategoryListItem(
|
||||
category: String,
|
||||
onRename: (String) -> Unit,
|
||||
onDelete: (String) -> Unit,
|
||||
) {
|
||||
ElevatedCard {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(start = horizontalPadding, top = horizontalPadding, end = horizontalPadding),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(imageVector = Icons.Outlined.Label, contentDescription = "")
|
||||
Text(text = category, modifier = Modifier.padding(start = horizontalPadding))
|
||||
}
|
||||
Row {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
IconButton(onClick = { onRename(category) }) {
|
||||
Icon(imageVector = Icons.Outlined.Edit, contentDescription = "")
|
||||
}
|
||||
IconButton(onClick = { onDelete(category) }) {
|
||||
Icon(imageVector = Icons.Outlined.Delete, contentDescription = "")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,8 +60,8 @@ fun ChapterDownloadIndicator(
|
||||
},
|
||||
) {
|
||||
val indicatorModifier = Modifier
|
||||
.size(IndicatorSize)
|
||||
.padding(IndicatorPadding)
|
||||
.size(IndicatorSize)
|
||||
.padding(IndicatorPadding)
|
||||
if (isDownloaded) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.CheckCircle,
|
||||
@@ -151,5 +151,6 @@ fun ChapterDownloadIndicator(
|
||||
|
||||
private val IndicatorSize = 26.dp
|
||||
private val IndicatorPadding = 2.dp
|
||||
|
||||
// To match composable parameter name when used later
|
||||
private val IndicatorStrokeWidth = IndicatorPadding
|
||||
|
||||
@@ -3,6 +3,12 @@ package eu.kanade.presentation.util
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
val horizontalPadding = 16.dp
|
||||
private val horizontal = 16.dp
|
||||
|
||||
val topPaddingValues = PaddingValues(top = 8.dp)
|
||||
private val vertical = 8.dp
|
||||
|
||||
val horizontalPadding = horizontal
|
||||
|
||||
val verticalPadding = vertical
|
||||
|
||||
val topPaddingValues = PaddingValues(top = vertical)
|
||||
|
||||
Reference in New Issue
Block a user