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:
zbue 2023-01-15 07:24:57 +08:00 committed by Jobobby04
parent 843c0a4588
commit 7f7789792b
21 changed files with 76 additions and 69 deletions

View File

@ -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()
} }
} }

View File

@ -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()
} }
} }

View File

@ -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) }
}
} }

View File

@ -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()
} }

View File

@ -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 -> {}
} }

View File

@ -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,
) )
}, },

View File

@ -44,6 +44,7 @@ fun DeleteLibraryMangaDialog(
}, },
confirmButton = { confirmButton = {
TextButton( TextButton(
enabled = list.any { it.isChecked },
onClick = { onClick = {
onDismissRequest() onDismissRequest()
onConfirm( onConfirm(

View File

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

View File

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

View File

@ -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))
} }
}, },

View File

@ -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
} }

View File

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

View File

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

View File

@ -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)
} }

View File

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

View File

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

View File

@ -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)
} }

View File

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

View File

@ -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)
} }

View File

@ -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
} }
} }

View File

@ -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" -->