Enable confirmButton
only when needed to respond to user input (#8848)
* Enable `confirmButton` when appropriate * Show error in dialog instead * Follow M3 guidelines (cherry picked from commit 33a221971692c1662dc883a7bac9fdcc7b843d35) # Conflicts: # app/src/main/java/eu/kanade/domain/category/interactor/CreateCategoryWithName.kt # app/src/main/java/eu/kanade/presentation/category/components/CategoryDialogs.kt # app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryScreen.kt
This commit is contained in:
parent
843c0a4588
commit
7f7789792b
@ -1,7 +1,6 @@
|
|||||||
package eu.kanade.domain.category.interactor
|
package eu.kanade.domain.category.interactor
|
||||||
|
|
||||||
import eu.kanade.domain.category.model.Category
|
import eu.kanade.domain.category.model.Category
|
||||||
import eu.kanade.domain.category.model.anyWithName
|
|
||||||
import eu.kanade.domain.category.repository.CategoryRepository
|
import eu.kanade.domain.category.repository.CategoryRepository
|
||||||
import eu.kanade.domain.library.service.LibraryPreferences
|
import eu.kanade.domain.library.service.LibraryPreferences
|
||||||
import eu.kanade.tachiyomi.util.lang.withNonCancellableContext
|
import eu.kanade.tachiyomi.util.lang.withNonCancellableContext
|
||||||
@ -23,10 +22,6 @@ class CreateCategoryWithName(
|
|||||||
|
|
||||||
suspend fun await(name: String): Result = withNonCancellableContext {
|
suspend fun await(name: String): Result = withNonCancellableContext {
|
||||||
val categories = categoryRepository.getAll()
|
val categories = categoryRepository.getAll()
|
||||||
if (categories.anyWithName(name)) {
|
|
||||||
return@withNonCancellableContext Result.NameAlreadyExistsError
|
|
||||||
}
|
|
||||||
|
|
||||||
val nextOrder = categories.maxOfOrNull { it.order }?.plus(1) ?: 0
|
val nextOrder = categories.maxOfOrNull { it.order }?.plus(1) ?: 0
|
||||||
val newCategory = Category(
|
val newCategory = Category(
|
||||||
id = 0,
|
id = 0,
|
||||||
@ -49,7 +44,6 @@ class CreateCategoryWithName(
|
|||||||
data class Success(val category: Category) : Result()
|
data class Success(val category: Category) : Result()
|
||||||
|
|
||||||
// SY <--
|
// SY <--
|
||||||
object NameAlreadyExistsError : Result()
|
|
||||||
data class InternalError(val error: Throwable) : Result()
|
data class InternalError(val error: Throwable) : Result()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package eu.kanade.domain.category.interactor
|
|||||||
|
|
||||||
import eu.kanade.domain.category.model.Category
|
import eu.kanade.domain.category.model.Category
|
||||||
import eu.kanade.domain.category.model.CategoryUpdate
|
import eu.kanade.domain.category.model.CategoryUpdate
|
||||||
import eu.kanade.domain.category.model.anyWithName
|
|
||||||
import eu.kanade.domain.category.repository.CategoryRepository
|
import eu.kanade.domain.category.repository.CategoryRepository
|
||||||
import eu.kanade.tachiyomi.util.lang.withNonCancellableContext
|
import eu.kanade.tachiyomi.util.lang.withNonCancellableContext
|
||||||
import eu.kanade.tachiyomi.util.system.logcat
|
import eu.kanade.tachiyomi.util.system.logcat
|
||||||
@ -13,11 +12,6 @@ class RenameCategory(
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun await(categoryId: Long, name: String) = withNonCancellableContext {
|
suspend fun await(categoryId: Long, name: String) = withNonCancellableContext {
|
||||||
val categories = categoryRepository.getAll()
|
|
||||||
if (categories.anyWithName(name)) {
|
|
||||||
return@withNonCancellableContext Result.NameAlreadyExistsError
|
|
||||||
}
|
|
||||||
|
|
||||||
val update = CategoryUpdate(
|
val update = CategoryUpdate(
|
||||||
id = categoryId,
|
id = categoryId,
|
||||||
name = name,
|
name = name,
|
||||||
@ -36,7 +30,6 @@ class RenameCategory(
|
|||||||
|
|
||||||
sealed class Result {
|
sealed class Result {
|
||||||
object Success : Result()
|
object Success : Result()
|
||||||
object NameAlreadyExistsError : Result()
|
|
||||||
data class InternalError(val error: Throwable) : Result()
|
data class InternalError(val error: Throwable) : Result()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,6 @@ import eu.kanade.tachiyomi.util.preference.plusAssign
|
|||||||
class CreateSourceCategory(private val preferences: SourcePreferences) {
|
class CreateSourceCategory(private val preferences: SourcePreferences) {
|
||||||
|
|
||||||
fun await(category: String): Result {
|
fun await(category: String): Result {
|
||||||
// Do not allow duplicate categories.
|
|
||||||
if (categoryExists(category)) {
|
|
||||||
return Result.CategoryExists
|
|
||||||
}
|
|
||||||
|
|
||||||
if (category.contains("|")) {
|
if (category.contains("|")) {
|
||||||
return Result.InvalidName
|
return Result.InvalidName
|
||||||
}
|
}
|
||||||
@ -22,15 +17,7 @@ class CreateSourceCategory(private val preferences: SourcePreferences) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sealed class Result {
|
sealed class Result {
|
||||||
object CategoryExists : Result()
|
|
||||||
object InvalidName : Result()
|
object InvalidName : Result()
|
||||||
object Success : Result()
|
object Success : Result()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if a repo with the given name already exists.
|
|
||||||
*/
|
|
||||||
private fun categoryExists(name: String): Boolean {
|
|
||||||
return preferences.sourcesTabCategories().get().any { it.equals(name, true) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,6 @@ import eu.kanade.tachiyomi.util.preference.plusAssign
|
|||||||
class CreateSourceRepo(private val preferences: UnsortedPreferences) {
|
class CreateSourceRepo(private val preferences: UnsortedPreferences) {
|
||||||
|
|
||||||
fun await(name: String): Result {
|
fun await(name: String): Result {
|
||||||
// Do not allow duplicate repos.
|
|
||||||
if (repoExists(name)) {
|
|
||||||
return Result.RepoExists
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do not allow invalid formats
|
// Do not allow invalid formats
|
||||||
if (!name.matches(repoRegex)) {
|
if (!name.matches(repoRegex)) {
|
||||||
return Result.InvalidName
|
return Result.InvalidName
|
||||||
@ -22,7 +17,6 @@ class CreateSourceRepo(private val preferences: UnsortedPreferences) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sealed class Result {
|
sealed class Result {
|
||||||
object RepoExists : Result()
|
|
||||||
object InvalidName : Result()
|
object InvalidName : Result()
|
||||||
object Success : Result()
|
object Success : Result()
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ class RenameSourceCategory(
|
|||||||
|
|
||||||
fun await(categoryOld: String, categoryNew: String): CreateSourceCategory.Result {
|
fun await(categoryOld: String, categoryNew: String): CreateSourceCategory.Result {
|
||||||
when (val result = createSourceCategory.await(categoryNew)) {
|
when (val result = createSourceCategory.await(categoryNew)) {
|
||||||
CreateSourceCategory.Result.CategoryExists -> return result
|
|
||||||
CreateSourceCategory.Result.InvalidName -> return result
|
CreateSourceCategory.Result.InvalidName -> return result
|
||||||
CreateSourceCategory.Result.Success -> {}
|
CreateSourceCategory.Result.Success -> {}
|
||||||
}
|
}
|
||||||
|
@ -24,20 +24,27 @@ fun CategoryCreateDialog(
|
|||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
onCreate: (String) -> Unit,
|
onCreate: (String) -> Unit,
|
||||||
// SY -->
|
// SY -->
|
||||||
|
categories: List<String>,
|
||||||
title: String,
|
title: String,
|
||||||
extraMessage: String? = null,
|
extraMessage: String? = null,
|
||||||
|
alreadyExistsError: Int = R.string.error_category_exists,
|
||||||
// SY <--
|
// SY <--
|
||||||
) {
|
) {
|
||||||
var name by remember { mutableStateOf("") }
|
var name by remember { mutableStateOf("") }
|
||||||
|
|
||||||
val focusRequester = remember { FocusRequester() }
|
val focusRequester = remember { FocusRequester() }
|
||||||
|
val nameAlreadyExists = remember(name) { categories.contains(name) }
|
||||||
|
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
TextButton(onClick = {
|
TextButton(
|
||||||
|
enabled = name.isNotEmpty() && !nameAlreadyExists,
|
||||||
|
onClick = {
|
||||||
onCreate(name)
|
onCreate(name)
|
||||||
onDismissRequest()
|
onDismissRequest()
|
||||||
},) {
|
},
|
||||||
|
) {
|
||||||
Text(text = stringResource(R.string.action_add))
|
Text(text = stringResource(R.string.action_add))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -64,6 +71,11 @@ fun CategoryCreateDialog(
|
|||||||
label = {
|
label = {
|
||||||
Text(text = stringResource(R.string.name))
|
Text(text = stringResource(R.string.name))
|
||||||
},
|
},
|
||||||
|
supportingText = {
|
||||||
|
val msgRes = if (name.isNotEmpty() && nameAlreadyExists) alreadyExistsError else R.string.information_required_plain
|
||||||
|
Text(text = stringResource(msgRes))
|
||||||
|
},
|
||||||
|
isError = name.isNotEmpty() && nameAlreadyExists,
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
)
|
)
|
||||||
// SY -->
|
// SY -->
|
||||||
@ -83,18 +95,27 @@ fun CategoryCreateDialog(
|
|||||||
fun CategoryRenameDialog(
|
fun CategoryRenameDialog(
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
onRename: (String) -> Unit,
|
onRename: (String) -> Unit,
|
||||||
|
// SY -->
|
||||||
|
categories: List<String>,
|
||||||
category: String,
|
category: String,
|
||||||
|
alreadyExistsError: Int = R.string.error_category_exists,
|
||||||
|
// SY <--
|
||||||
) {
|
) {
|
||||||
var name by remember { mutableStateOf(category) }
|
var name by remember { mutableStateOf(category) }
|
||||||
|
var valueHasChanged by remember { mutableStateOf(false) }
|
||||||
val focusRequester = remember { FocusRequester() }
|
val focusRequester = remember { FocusRequester() }
|
||||||
|
val nameAlreadyExists = remember(name) { categories.contains(name) }
|
||||||
|
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
TextButton(onClick = {
|
TextButton(
|
||||||
|
enabled = valueHasChanged && !nameAlreadyExists,
|
||||||
|
onClick = {
|
||||||
onRename(name)
|
onRename(name)
|
||||||
onDismissRequest()
|
onDismissRequest()
|
||||||
},) {
|
},
|
||||||
|
) {
|
||||||
Text(text = stringResource(android.R.string.ok))
|
Text(text = stringResource(android.R.string.ok))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -108,13 +129,18 @@ fun CategoryRenameDialog(
|
|||||||
},
|
},
|
||||||
text = {
|
text = {
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
modifier = Modifier
|
modifier = Modifier.focusRequester(focusRequester),
|
||||||
.focusRequester(focusRequester),
|
|
||||||
value = name,
|
value = name,
|
||||||
onValueChange = { name = it },
|
onValueChange = {
|
||||||
label = {
|
valueHasChanged = name != it
|
||||||
Text(text = stringResource(R.string.name))
|
name = it
|
||||||
},
|
},
|
||||||
|
label = { Text(text = stringResource(R.string.name)) },
|
||||||
|
supportingText = {
|
||||||
|
val msgRes = if (valueHasChanged && nameAlreadyExists) alreadyExistsError else R.string.information_required_plain
|
||||||
|
Text(text = stringResource(msgRes))
|
||||||
|
},
|
||||||
|
isError = valueHasChanged && nameAlreadyExists,
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -44,6 +44,7 @@ fun DeleteLibraryMangaDialog(
|
|||||||
},
|
},
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
TextButton(
|
TextButton(
|
||||||
|
enabled = list.any { it.isChecked },
|
||||||
onClick = {
|
onClick = {
|
||||||
onDismissRequest()
|
onDismissRequest()
|
||||||
onConfirm(
|
onConfirm(
|
||||||
|
@ -42,6 +42,7 @@ fun DownloadCustomAmountDialog(
|
|||||||
},
|
},
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
TextButton(
|
TextButton(
|
||||||
|
enabled = amount != 0,
|
||||||
onClick = {
|
onClick = {
|
||||||
onDismissRequest()
|
onDismissRequest()
|
||||||
onConfirm(amount.coerceIn(0, maxAmount))
|
onConfirm(amount.coerceIn(0, maxAmount))
|
||||||
|
@ -334,10 +334,6 @@ object SettingsAdvancedScreen : SearchableSettings {
|
|||||||
pref = userAgentPref,
|
pref = userAgentPref,
|
||||||
title = stringResource(R.string.pref_user_agent_string),
|
title = stringResource(R.string.pref_user_agent_string),
|
||||||
onValueChanged = {
|
onValueChanged = {
|
||||||
if (it.isBlank()) {
|
|
||||||
context.toast(R.string.error_user_agent_string_blank)
|
|
||||||
return@EditTextPreference false
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
// OkHttp checks for valid values internally
|
// OkHttp checks for valid values internally
|
||||||
Headers.Builder().add("User-Agent", it)
|
Headers.Builder().add("User-Agent", it)
|
||||||
|
@ -336,7 +336,10 @@ object SettingsLibraryScreen : SearchableSettings {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
TextButton(onClick = { onValueChanged(portraitValue, landscapeValue) }) {
|
TextButton(
|
||||||
|
enabled = portraitValue != initialPortrait || landscapeValue != initialLandscape,
|
||||||
|
onClick = { onValueChanged(portraitValue, landscapeValue) },
|
||||||
|
) {
|
||||||
Text(text = stringResource(android.R.string.ok))
|
Text(text = stringResource(android.R.string.ok))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -222,7 +222,7 @@ object SettingsTrackingScreen : SearchableSettings {
|
|||||||
label = { Text(text = stringResource(uNameStringRes)) },
|
label = { Text(text = stringResource(uNameStringRes)) },
|
||||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
|
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
isError = inputError && username.text.isEmpty(),
|
isError = inputError && !processing,
|
||||||
)
|
)
|
||||||
|
|
||||||
var hidePassword by remember { mutableStateOf(true) }
|
var hidePassword by remember { mutableStateOf(true) }
|
||||||
@ -253,21 +253,16 @@ object SettingsTrackingScreen : SearchableSettings {
|
|||||||
imeAction = ImeAction.Done,
|
imeAction = ImeAction.Done,
|
||||||
),
|
),
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
isError = inputError && password.text.isEmpty(),
|
isError = inputError && !processing,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
Button(
|
Button(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
enabled = !processing,
|
enabled = !processing && username.text.isNotBlank() && password.text.isNotBlank(),
|
||||||
onClick = {
|
onClick = {
|
||||||
if (username.text.isEmpty() || password.text.isEmpty()) {
|
|
||||||
inputError = true
|
|
||||||
return@Button
|
|
||||||
}
|
|
||||||
scope.launchIO {
|
scope.launchIO {
|
||||||
inputError = false
|
|
||||||
processing = true
|
processing = true
|
||||||
val result = checkLogin(
|
val result = checkLogin(
|
||||||
context = context,
|
context = context,
|
||||||
@ -275,6 +270,7 @@ object SettingsTrackingScreen : SearchableSettings {
|
|||||||
username = username.text,
|
username = username.text,
|
||||||
password = password.text,
|
password = password.text,
|
||||||
)
|
)
|
||||||
|
inputError = !result
|
||||||
if (result) onDismissRequest()
|
if (result) onDismissRequest()
|
||||||
processing = false
|
processing = false
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
package eu.kanade.presentation.more.settings.widget
|
package eu.kanade.presentation.more.settings.widget
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Cancel
|
||||||
|
import androidx.compose.material.icons.filled.Error
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
@ -50,6 +55,16 @@ fun EditTextPreferenceWidget(
|
|||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = textFieldValue,
|
value = textFieldValue,
|
||||||
onValueChange = { textFieldValue = it },
|
onValueChange = { textFieldValue = it },
|
||||||
|
trailingIcon = {
|
||||||
|
if (textFieldValue.text.isBlank()) {
|
||||||
|
Icon(imageVector = Icons.Filled.Error, contentDescription = null)
|
||||||
|
} else {
|
||||||
|
IconButton(onClick = { textFieldValue = TextFieldValue("") }) {
|
||||||
|
Icon(imageVector = Icons.Filled.Cancel, contentDescription = null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isError = textFieldValue.text.isBlank(),
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
)
|
)
|
||||||
@ -59,6 +74,7 @@ fun EditTextPreferenceWidget(
|
|||||||
),
|
),
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
TextButton(
|
TextButton(
|
||||||
|
enabled = textFieldValue.text != value && textFieldValue.text.isNotBlank(),
|
||||||
onClick = {
|
onClick = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
if (onConfirm(textFieldValue.text)) {
|
if (onConfirm(textFieldValue.text)) {
|
||||||
|
@ -6,6 +6,7 @@ import androidx.compose.runtime.collectAsState
|
|||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.util.fastMap
|
||||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||||
import cafe.adriel.voyager.core.screen.Screen
|
import cafe.adriel.voyager.core.screen.Screen
|
||||||
import cafe.adriel.voyager.core.screen.uniqueScreenKey
|
import cafe.adriel.voyager.core.screen.uniqueScreenKey
|
||||||
@ -54,8 +55,9 @@ class CategoryScreen : Screen {
|
|||||||
CategoryDialog.Create -> {
|
CategoryDialog.Create -> {
|
||||||
CategoryCreateDialog(
|
CategoryCreateDialog(
|
||||||
onDismissRequest = screenModel::dismissDialog,
|
onDismissRequest = screenModel::dismissDialog,
|
||||||
onCreate = { screenModel.createCategory(it) },
|
onCreate = screenModel::createCategory,
|
||||||
// SY -->
|
// SY -->
|
||||||
|
categories = successState.categories.fastMap { it.name },
|
||||||
title = stringResource(R.string.action_add_category),
|
title = stringResource(R.string.action_add_category),
|
||||||
// SY <--
|
// SY <--
|
||||||
)
|
)
|
||||||
@ -65,6 +67,7 @@ class CategoryScreen : Screen {
|
|||||||
onDismissRequest = screenModel::dismissDialog,
|
onDismissRequest = screenModel::dismissDialog,
|
||||||
onRename = { screenModel.renameCategory(dialog.category, it) },
|
onRename = { screenModel.renameCategory(dialog.category, it) },
|
||||||
// SY -->
|
// SY -->
|
||||||
|
categories = successState.categories.fastMap { it.name },
|
||||||
category = dialog.category.name,
|
category = dialog.category.name,
|
||||||
// SY <--
|
// SY <--
|
||||||
)
|
)
|
||||||
|
@ -47,7 +47,6 @@ class CategoryScreenModel(
|
|||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
when (createCategoryWithName.await(name)) {
|
when (createCategoryWithName.await(name)) {
|
||||||
is CreateCategoryWithName.Result.InternalError -> _events.send(CategoryEvent.InternalError)
|
is CreateCategoryWithName.Result.InternalError -> _events.send(CategoryEvent.InternalError)
|
||||||
CreateCategoryWithName.Result.NameAlreadyExistsError -> _events.send(CategoryEvent.CategoryWithNameAlreadyExists)
|
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,7 +83,6 @@ class CategoryScreenModel(
|
|||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
when (renameCategory.await(category, name)) {
|
when (renameCategory.await(category, name)) {
|
||||||
is RenameCategory.Result.InternalError -> _events.send(CategoryEvent.InternalError)
|
is RenameCategory.Result.InternalError -> _events.send(CategoryEvent.InternalError)
|
||||||
RenameCategory.Result.NameAlreadyExistsError -> _events.send(CategoryEvent.CategoryWithNameAlreadyExists)
|
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -117,7 +115,6 @@ sealed class CategoryDialog {
|
|||||||
|
|
||||||
sealed class CategoryEvent {
|
sealed class CategoryEvent {
|
||||||
sealed class LocalizedMessage(@StringRes val stringRes: Int) : CategoryEvent()
|
sealed class LocalizedMessage(@StringRes val stringRes: Int) : CategoryEvent()
|
||||||
object CategoryWithNameAlreadyExists : LocalizedMessage(R.string.error_category_exists)
|
|
||||||
object InternalError : LocalizedMessage(R.string.internal_error)
|
object InternalError : LocalizedMessage(R.string.internal_error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,8 +49,10 @@ class SortTagScreen : Screen {
|
|||||||
CategoryCreateDialog(
|
CategoryCreateDialog(
|
||||||
onDismissRequest = screenModel::dismissDialog,
|
onDismissRequest = screenModel::dismissDialog,
|
||||||
onCreate = { screenModel.createTag(it) },
|
onCreate = { screenModel.createTag(it) },
|
||||||
|
categories = successState.tags,
|
||||||
title = stringResource(R.string.add_tag),
|
title = stringResource(R.string.add_tag),
|
||||||
extraMessage = stringResource(R.string.action_add_tags_message),
|
extraMessage = stringResource(R.string.action_add_tags_message),
|
||||||
|
alreadyExistsError = R.string.error_tag_exists,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is SortTagDialog.Delete -> {
|
is SortTagDialog.Delete -> {
|
||||||
|
@ -48,8 +48,10 @@ class RepoScreen : Screen {
|
|||||||
CategoryCreateDialog(
|
CategoryCreateDialog(
|
||||||
onDismissRequest = screenModel::dismissDialog,
|
onDismissRequest = screenModel::dismissDialog,
|
||||||
onCreate = { screenModel.createRepo(it) },
|
onCreate = { screenModel.createRepo(it) },
|
||||||
|
categories = successState.repos,
|
||||||
title = stringResource(R.string.action_add_repo),
|
title = stringResource(R.string.action_add_repo),
|
||||||
extraMessage = stringResource(R.string.action_add_repo_message),
|
extraMessage = stringResource(R.string.action_add_repo_message),
|
||||||
|
alreadyExistsError = R.string.error_repo_exists,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is RepoDialog.Delete -> {
|
is RepoDialog.Delete -> {
|
||||||
|
@ -46,7 +46,6 @@ class RepoScreenModel(
|
|||||||
fun createRepo(name: String) {
|
fun createRepo(name: String) {
|
||||||
coroutineScope.launchIO {
|
coroutineScope.launchIO {
|
||||||
when (createSourceRepo.await(name)) {
|
when (createSourceRepo.await(name)) {
|
||||||
is CreateSourceRepo.Result.RepoExists -> _events.send(RepoEvent.RepoExists)
|
|
||||||
is CreateSourceRepo.Result.InvalidName -> _events.send(RepoEvent.InvalidName)
|
is CreateSourceRepo.Result.InvalidName -> _events.send(RepoEvent.InvalidName)
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
@ -85,7 +84,6 @@ class RepoScreenModel(
|
|||||||
|
|
||||||
sealed class RepoEvent {
|
sealed class RepoEvent {
|
||||||
sealed class LocalizedMessage(@StringRes val stringRes: Int) : RepoEvent()
|
sealed class LocalizedMessage(@StringRes val stringRes: Int) : RepoEvent()
|
||||||
object RepoExists : LocalizedMessage(R.string.error_repo_exists)
|
|
||||||
object InvalidName : LocalizedMessage(R.string.invalid_repo_name)
|
object InvalidName : LocalizedMessage(R.string.invalid_repo_name)
|
||||||
object InternalError : LocalizedMessage(R.string.internal_error)
|
object InternalError : LocalizedMessage(R.string.internal_error)
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,7 @@ class SourceCategoryScreen : Screen {
|
|||||||
onDismissRequest = screenModel::dismissDialog,
|
onDismissRequest = screenModel::dismissDialog,
|
||||||
onCreate = { screenModel.createCategory(it) },
|
onCreate = { screenModel.createCategory(it) },
|
||||||
// SY -->
|
// SY -->
|
||||||
|
categories = successState.categories,
|
||||||
title = stringResource(R.string.action_add_category),
|
title = stringResource(R.string.action_add_category),
|
||||||
// SY <--
|
// SY <--
|
||||||
)
|
)
|
||||||
@ -60,6 +61,7 @@ class SourceCategoryScreen : Screen {
|
|||||||
onDismissRequest = screenModel::dismissDialog,
|
onDismissRequest = screenModel::dismissDialog,
|
||||||
onRename = { screenModel.renameCategory(dialog.category, it) },
|
onRename = { screenModel.renameCategory(dialog.category, it) },
|
||||||
// SY -->
|
// SY -->
|
||||||
|
categories = successState.categories,
|
||||||
category = dialog.category,
|
category = dialog.category,
|
||||||
// SY <--
|
// SY <--
|
||||||
)
|
)
|
||||||
|
@ -48,7 +48,6 @@ class SourceCategoryScreenModel(
|
|||||||
fun createCategory(name: String) {
|
fun createCategory(name: String) {
|
||||||
coroutineScope.launchIO {
|
coroutineScope.launchIO {
|
||||||
when (createSourceCategory.await(name)) {
|
when (createSourceCategory.await(name)) {
|
||||||
is CreateSourceCategory.Result.CategoryExists -> _events.send(SourceCategoryEvent.CategoryExists)
|
|
||||||
is CreateSourceCategory.Result.InvalidName -> _events.send(SourceCategoryEvent.InvalidName)
|
is CreateSourceCategory.Result.InvalidName -> _events.send(SourceCategoryEvent.InvalidName)
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
@ -75,7 +74,6 @@ class SourceCategoryScreenModel(
|
|||||||
fun renameCategory(categoryOld: String, categoryNew: String) {
|
fun renameCategory(categoryOld: String, categoryNew: String) {
|
||||||
coroutineScope.launchIO {
|
coroutineScope.launchIO {
|
||||||
when (renameSourceCategory.await(categoryOld, categoryNew)) {
|
when (renameSourceCategory.await(categoryOld, categoryNew)) {
|
||||||
is CreateSourceCategory.Result.CategoryExists -> _events.send(SourceCategoryEvent.CategoryExists)
|
|
||||||
is CreateSourceCategory.Result.InvalidName -> _events.send(SourceCategoryEvent.InvalidName)
|
is CreateSourceCategory.Result.InvalidName -> _events.send(SourceCategoryEvent.InvalidName)
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
@ -103,7 +101,6 @@ class SourceCategoryScreenModel(
|
|||||||
|
|
||||||
sealed class SourceCategoryEvent {
|
sealed class SourceCategoryEvent {
|
||||||
sealed class LocalizedMessage(@StringRes val stringRes: Int) : SourceCategoryEvent()
|
sealed class LocalizedMessage(@StringRes val stringRes: Int) : SourceCategoryEvent()
|
||||||
object CategoryExists : LocalizedMessage(R.string.error_category_exists)
|
|
||||||
object InvalidName : LocalizedMessage(R.string.invalid_category_name)
|
object InvalidName : LocalizedMessage(R.string.invalid_category_name)
|
||||||
object InternalError : LocalizedMessage(R.string.internal_error)
|
object InternalError : LocalizedMessage(R.string.internal_error)
|
||||||
}
|
}
|
||||||
|
@ -206,7 +206,6 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
val local = localCategories.getOrElse(index) {
|
val local = localCategories.getOrElse(index) {
|
||||||
when (val createCategoryWithNameResult = createCategoryWithName.await(remote)) {
|
when (val createCategoryWithNameResult = createCategoryWithName.await(remote)) {
|
||||||
is CreateCategoryWithName.Result.InternalError -> throw createCategoryWithNameResult.error
|
is CreateCategoryWithName.Result.InternalError -> throw createCategoryWithNameResult.error
|
||||||
CreateCategoryWithName.Result.NameAlreadyExistsError -> throw IllegalStateException("Category $remote already exists")
|
|
||||||
is CreateCategoryWithName.Result.Success -> createCategoryWithNameResult.category
|
is CreateCategoryWithName.Result.Success -> createCategoryWithNameResult.category
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -882,6 +882,7 @@
|
|||||||
<string name="information_empty_category">You have no categories. Tap the plus button to create one for organizing your library.</string>
|
<string name="information_empty_category">You have no categories. Tap the plus button to create one for organizing your library.</string>
|
||||||
<string name="information_empty_category_dialog">You don\'t have any categories yet.</string>
|
<string name="information_empty_category_dialog">You don\'t have any categories yet.</string>
|
||||||
<string name="information_cloudflare_bypass_failure">Failed to bypass Cloudflare</string>
|
<string name="information_cloudflare_bypass_failure">Failed to bypass Cloudflare</string>
|
||||||
|
<string name="information_required_plain">*required</string>
|
||||||
<!-- Do not translate "WebView" -->
|
<!-- Do not translate "WebView" -->
|
||||||
<string name="information_webview_required">WebView is required for Tachiyomi</string>
|
<string name="information_webview_required">WebView is required for Tachiyomi</string>
|
||||||
<!-- Do not translate "WebView" -->
|
<!-- Do not translate "WebView" -->
|
||||||
|
Loading…
x
Reference in New Issue
Block a user