Support external repos

Largely taken from SY.

Co-authored-by: jobobby04 <jobobby04@users.noreply.github.com>
(cherry picked from commit c17ada2c98041877ab901efb9b03497130ead34a)

# Conflicts:
#	app/src/main/java/eu/kanade/domain/source/interactor/CreateSourceRepo.kt
#	app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt
#	app/src/main/java/eu/kanade/presentation/category/SourceRepoScreen.kt
#	app/src/main/java/eu/kanade/presentation/category/components/CategoryDialogs.kt
#	app/src/main/java/eu/kanade/presentation/category/components/repo/SourceRepoContent.kt
#	app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt
#	app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionGithubApi.kt
#	app/src/main/java/eu/kanade/tachiyomi/extension/model/Extension.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryScreen.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt
This commit is contained in:
arkon 2024-01-05 17:28:08 -05:00 committed by Jobobby04
parent b677f81fb1
commit 24a56a5529
35 changed files with 133 additions and 244 deletions

View File

@ -11,8 +11,11 @@ import eu.kanade.domain.manga.interactor.GetExcludedScanlators
import eu.kanade.domain.manga.interactor.SetExcludedScanlators import eu.kanade.domain.manga.interactor.SetExcludedScanlators
import eu.kanade.domain.manga.interactor.SetMangaViewerFlags import eu.kanade.domain.manga.interactor.SetMangaViewerFlags
import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.domain.source.interactor.CreateSourceRepo
import eu.kanade.domain.source.interactor.DeleteSourceRepos
import eu.kanade.domain.source.interactor.GetEnabledSources import eu.kanade.domain.source.interactor.GetEnabledSources
import eu.kanade.domain.source.interactor.GetLanguagesWithSources import eu.kanade.domain.source.interactor.GetLanguagesWithSources
import eu.kanade.domain.source.interactor.GetSourceRepos
import eu.kanade.domain.source.interactor.GetSourcesWithFavoriteCount import eu.kanade.domain.source.interactor.GetSourcesWithFavoriteCount
import eu.kanade.domain.source.interactor.SetMigrateSorting import eu.kanade.domain.source.interactor.SetMigrateSorting
import eu.kanade.domain.source.interactor.ToggleLanguage import eu.kanade.domain.source.interactor.ToggleLanguage
@ -167,5 +170,9 @@ class DomainModule : InjektModule {
addFactory { ToggleLanguage(get()) } addFactory { ToggleLanguage(get()) }
addFactory { ToggleSource(get()) } addFactory { ToggleSource(get()) }
addFactory { ToggleSourcePin(get()) } addFactory { ToggleSourcePin(get()) }
addFactory { CreateSourceRepo(get()) }
addFactory { DeleteSourceRepos(get()) }
addFactory { GetSourceRepos(get()) }
} }
} }

View File

@ -7,7 +7,6 @@ import eu.kanade.domain.manga.interactor.GetPagePreviews
import eu.kanade.domain.manga.interactor.GetSortTag import eu.kanade.domain.manga.interactor.GetSortTag
import eu.kanade.domain.manga.interactor.ReorderSortTag import eu.kanade.domain.manga.interactor.ReorderSortTag
import eu.kanade.domain.source.interactor.CreateSourceCategory import eu.kanade.domain.source.interactor.CreateSourceCategory
import eu.kanade.domain.source.interactor.CreateSourceRepo
import eu.kanade.domain.source.interactor.DeleteSourceCategory import eu.kanade.domain.source.interactor.DeleteSourceCategory
import eu.kanade.domain.source.interactor.GetExhSavedSearch import eu.kanade.domain.source.interactor.GetExhSavedSearch
import eu.kanade.domain.source.interactor.GetShowLatest import eu.kanade.domain.source.interactor.GetShowLatest
@ -61,14 +60,12 @@ import tachiyomi.domain.source.interactor.CountFeedSavedSearchBySourceId
import tachiyomi.domain.source.interactor.CountFeedSavedSearchGlobal import tachiyomi.domain.source.interactor.CountFeedSavedSearchGlobal
import tachiyomi.domain.source.interactor.DeleteFeedSavedSearchById import tachiyomi.domain.source.interactor.DeleteFeedSavedSearchById
import tachiyomi.domain.source.interactor.DeleteSavedSearchById import tachiyomi.domain.source.interactor.DeleteSavedSearchById
import tachiyomi.domain.source.interactor.DeleteSourceRepos
import tachiyomi.domain.source.interactor.GetFeedSavedSearchBySourceId import tachiyomi.domain.source.interactor.GetFeedSavedSearchBySourceId
import tachiyomi.domain.source.interactor.GetFeedSavedSearchGlobal import tachiyomi.domain.source.interactor.GetFeedSavedSearchGlobal
import tachiyomi.domain.source.interactor.GetSavedSearchById import tachiyomi.domain.source.interactor.GetSavedSearchById
import tachiyomi.domain.source.interactor.GetSavedSearchBySourceId import tachiyomi.domain.source.interactor.GetSavedSearchBySourceId
import tachiyomi.domain.source.interactor.GetSavedSearchBySourceIdFeed import tachiyomi.domain.source.interactor.GetSavedSearchBySourceIdFeed
import tachiyomi.domain.source.interactor.GetSavedSearchGlobalFeed import tachiyomi.domain.source.interactor.GetSavedSearchGlobalFeed
import tachiyomi.domain.source.interactor.GetSourceRepos
import tachiyomi.domain.source.interactor.InsertFeedSavedSearch import tachiyomi.domain.source.interactor.InsertFeedSavedSearch
import tachiyomi.domain.source.interactor.InsertSavedSearch import tachiyomi.domain.source.interactor.InsertSavedSearch
import tachiyomi.domain.source.repository.FeedSavedSearchRepository import tachiyomi.domain.source.repository.FeedSavedSearchRepository
@ -94,9 +91,6 @@ class SYDomainModule : InjektModule {
addFactory { FilterSerializer() } addFactory { FilterSerializer() }
addFactory { GetHistoryByMangaId(get()) } addFactory { GetHistoryByMangaId(get()) }
addFactory { GetChapterByUrl(get()) } addFactory { GetChapterByUrl(get()) }
addFactory { CreateSourceRepo(get()) }
addFactory { DeleteSourceRepos(get()) }
addFactory { GetSourceRepos(get()) }
addFactory { GetSourceCategories(get()) } addFactory { GetSourceCategories(get()) }
addFactory { CreateSourceCategory(get()) } addFactory { CreateSourceCategory(get()) }
addFactory { RenameSourceCategory(get(), get()) } addFactory { RenameSourceCategory(get(), get()) }

View File

@ -1,9 +1,9 @@
package eu.kanade.domain.source.interactor package eu.kanade.domain.source.interactor
import eu.kanade.domain.source.service.SourcePreferences
import tachiyomi.core.preference.plusAssign import tachiyomi.core.preference.plusAssign
import tachiyomi.domain.UnsortedPreferences
class CreateSourceRepo(private val preferences: UnsortedPreferences) { class CreateSourceRepo(private val preferences: SourcePreferences) {
fun await(name: String): Result { fun await(name: String): Result {
// Do not allow invalid formats // Do not allow invalid formats

View File

@ -1,8 +1,8 @@
package tachiyomi.domain.source.interactor package eu.kanade.domain.source.interactor
import tachiyomi.domain.UnsortedPreferences import eu.kanade.domain.source.service.SourcePreferences
class DeleteSourceRepos(private val preferences: UnsortedPreferences) { class DeleteSourceRepos(private val preferences: SourcePreferences) {
fun await(repos: List<String>) { fun await(repos: List<String>) {
preferences.extensionRepos().set( preferences.extensionRepos().set(

View File

@ -1,10 +1,10 @@
package tachiyomi.domain.source.interactor package eu.kanade.domain.source.interactor
import eu.kanade.domain.source.service.SourcePreferences
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import tachiyomi.domain.UnsortedPreferences
class GetSourceRepos(private val preferences: UnsortedPreferences) { class GetSourceRepos(private val preferences: SourcePreferences) {
fun subscribe(): Flow<List<String>> { fun subscribe(): Flow<List<String>> {
return preferences.extensionRepos().changes().map { it.sortedWith(String.CASE_INSENSITIVE_ORDER) } return preferences.extensionRepos().changes().map { it.sortedWith(String.CASE_INSENSITIVE_ORDER) }

View File

@ -38,6 +38,8 @@ class SourcePreferences(
SetMigrateSorting.Direction.ASCENDING, SetMigrateSorting.Direction.ASCENDING,
) )
fun extensionRepos() = preferenceStore.getStringSet("extension_repos", emptySet())
fun extensionUpdatesCount() = preferenceStore.getInt("ext_updates_count", 0) fun extensionUpdatesCount() = preferenceStore.getInt("ext_updates_count", 0)
fun trustedSignatures() = preferenceStore.getStringSet(Preference.appStateKey("trusted_signatures"), emptySet()) fun trustedSignatures() = preferenceStore.getStringSet(Preference.appStateKey("trusted_signatures"), emptySet())

View File

@ -156,11 +156,12 @@ private fun ExtensionDetails(
item { item {
WarningBanner(SYMR.strings.redundant_extension_message) WarningBanner(SYMR.strings.redundant_extension_message)
} }
// SY <--
extension.isRepoSource -> extension.isRepoSource ->
item { item {
val uriHandler = LocalUriHandler.current val uriHandler = LocalUriHandler.current
WarningBanner( WarningBanner(
SYMR.strings.repo_extension_message, MR.strings.repo_extension_message,
modifier = Modifier.clickable { modifier = Modifier.clickable {
extension.repoUrl ?: return@clickable extension.repoUrl ?: return@clickable
uriHandler.openUri( uriHandler.openUri(
@ -171,7 +172,6 @@ private fun ExtensionDetails(
}, },
) )
} }
// SY <--
extension.isUnofficial -> extension.isUnofficial ->
item { item {
WarningBanner(MR.strings.unofficial_extension_message) WarningBanner(MR.strings.unofficial_extension_message)

View File

@ -340,13 +340,11 @@ private fun ExtensionItemContent(
val warning = when { val warning = when {
extension is Extension.Untrusted -> MR.strings.ext_untrusted extension is Extension.Untrusted -> MR.strings.ext_untrusted
// SY -->
extension is Extension.Installed && extension.isRepoSource -> SYMR.strings.repo_source
extension is Extension.Available && extension.isRepoSource -> SYMR.strings.repo_source
// SY <--
extension is Extension.Installed && extension.isUnofficial -> MR.strings.ext_unofficial extension is Extension.Installed && extension.isUnofficial -> MR.strings.ext_unofficial
extension is Extension.Installed && extension.isObsolete -> MR.strings.ext_obsolete extension is Extension.Installed && extension.isObsolete -> MR.strings.ext_obsolete
// SY -->
extension is Extension.Installed && extension.isRedundant -> SYMR.strings.ext_redundant extension is Extension.Installed && extension.isRedundant -> SYMR.strings.ext_redundant
// SY <--
extension.isNsfw -> MR.strings.ext_nsfw_short extension.isNsfw -> MR.strings.ext_nsfw_short
else -> null else -> null
} }

View File

@ -8,9 +8,9 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import eu.kanade.presentation.category.components.CategoryFloatingActionButton import eu.kanade.presentation.category.components.CategoryFloatingActionButton
import eu.kanade.presentation.category.components.repo.SourceRepoContent import eu.kanade.presentation.category.components.repo.SourceRepoContent
import eu.kanade.presentation.category.repos.RepoScreenState
import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBar
import eu.kanade.tachiyomi.ui.category.repos.RepoScreenState import tachiyomi.i18n.MR
import tachiyomi.i18n.sy.SYMR
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.components.material.topSmallPaddingValues import tachiyomi.presentation.core.components.material.topSmallPaddingValues
@ -30,7 +30,7 @@ fun SourceRepoScreen(
topBar = { scrollBehavior -> topBar = { scrollBehavior ->
AppBar( AppBar(
navigateUp = navigateUp, navigateUp = navigateUp,
title = stringResource(SYMR.strings.action_edit_repos), title = stringResource(MR.strings.label_extension_repos),
scrollBehavior = scrollBehavior, scrollBehavior = scrollBehavior,
) )
}, },
@ -43,7 +43,7 @@ fun SourceRepoScreen(
) { paddingValues -> ) { paddingValues ->
if (state.isEmpty) { if (state.isEmpty) {
EmptyScreen( EmptyScreen(
SYMR.strings.information_empty_repos, MR.strings.information_empty_repos,
modifier = Modifier.padding(paddingValues), modifier = Modifier.padding(paddingValues),
) )
return@Scaffold return@Scaffold

View File

@ -42,12 +42,10 @@ import kotlin.time.Duration.Companion.seconds
fun CategoryCreateDialog( fun CategoryCreateDialog(
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
onCreate: (String) -> Unit, onCreate: (String) -> Unit,
// SY -->
categories: ImmutableList<String>, categories: ImmutableList<String>,
title: String, title: String,
extraMessage: String? = null, extraMessage: String? = null,
alreadyExistsError: StringResource = MR.strings.error_category_exists, alreadyExistsError: StringResource = MR.strings.error_category_exists,
// SY <--
) { ) {
var name by remember { mutableStateOf("") } var name by remember { mutableStateOf("") }
@ -76,12 +74,9 @@ fun CategoryCreateDialog(
Text(text = title) Text(text = title)
}, },
text = { text = {
// SY -->
Column { Column {
if (extraMessage != null) { extraMessage?.let { Text(it) }
Text(extraMessage)
}
// SY <--
OutlinedTextField( OutlinedTextField(
modifier = Modifier modifier = Modifier
.focusRequester(focusRequester), .focusRequester(focusRequester),
@ -101,9 +96,7 @@ fun CategoryCreateDialog(
isError = name.isNotEmpty() && nameAlreadyExists, isError = name.isNotEmpty() && nameAlreadyExists,
singleLine = true, singleLine = true,
) )
// SY -->
} }
// SY <--
}, },
) )
@ -118,11 +111,9 @@ fun CategoryCreateDialog(
fun CategoryRenameDialog( fun CategoryRenameDialog(
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
onRename: (String) -> Unit, onRename: (String) -> Unit,
// SY -->
categories: ImmutableList<String>, categories: ImmutableList<String>,
category: String, category: String,
alreadyExistsError: StringResource = MR.strings.error_category_exists, alreadyExistsError: StringResource = MR.strings.error_category_exists,
// SY <--
) { ) {
var name by remember { mutableStateOf(category) } var name by remember { mutableStateOf(category) }
var valueHasChanged by remember { mutableStateOf(false) } var valueHasChanged by remember { mutableStateOf(false) }
@ -185,10 +176,8 @@ fun CategoryRenameDialog(
fun CategoryDeleteDialog( fun CategoryDeleteDialog(
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
onDelete: () -> Unit, onDelete: () -> Unit,
// SY -->
title: String, title: String,
text: String, text: String,
// SY <--
) { ) {
AlertDialog( AlertDialog(
onDismissRequest = onDismissRequest, onDismissRequest = onDismissRequest,
@ -351,7 +340,3 @@ fun ChangeCategoryDialog(
}, },
) )
} }
private fun List<Category>.anyWithName(name: String): Boolean {
return any { name == it.name }
}

View File

@ -2,11 +2,23 @@ package eu.kanade.presentation.category.components.repo
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.Label
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.padding
@ -17,11 +29,13 @@ fun SourceRepoContent(
lazyListState: LazyListState, lazyListState: LazyListState,
paddingValues: PaddingValues, paddingValues: PaddingValues,
onClickDelete: (String) -> Unit, onClickDelete: (String) -> Unit,
modifier: Modifier = Modifier,
) { ) {
LazyColumn( LazyColumn(
state = lazyListState, state = lazyListState,
contentPadding = paddingValues, contentPadding = paddingValues,
verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small), verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
modifier = modifier,
) { ) {
items(repos) { repo -> items(repos) { repo ->
SourceRepoListItem( SourceRepoListItem(
@ -32,3 +46,34 @@ fun SourceRepoContent(
} }
} }
} }
@Composable
private fun SourceRepoListItem(
repo: String,
onDelete: () -> Unit,
modifier: Modifier = Modifier,
) {
ElevatedCard(
modifier = modifier,
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(
start = MaterialTheme.padding.medium,
top = MaterialTheme.padding.medium,
end = MaterialTheme.padding.medium,
),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(imageVector = Icons.AutoMirrored.Outlined.Label, contentDescription = "")
Text(text = repo, modifier = Modifier.padding(start = MaterialTheme.padding.medium))
}
Row {
Spacer(modifier = Modifier.weight(1f))
IconButton(onClick = onDelete) {
Icon(imageVector = Icons.Outlined.Delete, contentDescription = "")
}
}
}
}

View File

@ -1,50 +0,0 @@
package eu.kanade.presentation.category.components.repo
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.Label
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.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import tachiyomi.presentation.core.components.material.padding
@Composable
fun SourceRepoListItem(
modifier: Modifier,
repo: String,
onDelete: () -> Unit,
) {
ElevatedCard(
modifier = modifier,
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(
start = MaterialTheme.padding.medium,
top = MaterialTheme.padding.medium,
end = MaterialTheme.padding.medium,
),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(imageVector = Icons.AutoMirrored.Outlined.Label, contentDescription = "")
Text(text = repo, modifier = Modifier.padding(start = MaterialTheme.padding.medium))
}
Row {
Spacer(modifier = Modifier.weight(1f))
IconButton(onClick = onDelete) {
Icon(imageVector = Icons.Outlined.Delete, contentDescription = "")
}
}
}
}

View File

@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.ui.category.repos package eu.kanade.presentation.category.repos
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
@ -14,11 +14,12 @@ import eu.kanade.presentation.category.components.CategoryDeleteDialog
import eu.kanade.presentation.util.Screen import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import tachiyomi.i18n.sy.SYMR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.screens.LoadingScreen import tachiyomi.presentation.core.screens.LoadingScreen
class RepoScreen : Screen() { class RepoScreen : Screen() {
@Composable @Composable
override fun Content() { override fun Content() {
val context = LocalContext.current val context = LocalContext.current
@ -48,17 +49,17 @@ class RepoScreen : Screen() {
onDismissRequest = screenModel::dismissDialog, onDismissRequest = screenModel::dismissDialog,
onCreate = { screenModel.createRepo(it) }, onCreate = { screenModel.createRepo(it) },
categories = successState.repos, categories = successState.repos,
title = stringResource(SYMR.strings.action_add_repo), title = stringResource(MR.strings.action_add_repo),
extraMessage = stringResource(SYMR.strings.action_add_repo_message), extraMessage = stringResource(MR.strings.action_add_repo_message),
alreadyExistsError = SYMR.strings.error_repo_exists, alreadyExistsError = MR.strings.error_repo_exists,
) )
} }
is RepoDialog.Delete -> { is RepoDialog.Delete -> {
CategoryDeleteDialog( CategoryDeleteDialog(
onDismissRequest = screenModel::dismissDialog, onDismissRequest = screenModel::dismissDialog,
onDelete = { screenModel.deleteRepos(listOf(dialog.repo)) }, onDelete = { screenModel.deleteRepos(listOf(dialog.repo)) },
title = stringResource(SYMR.strings.delete_repo), title = stringResource(MR.strings.action_delete_repo),
text = stringResource(SYMR.strings.delete_repo_confirmation, dialog.repo), text = stringResource(MR.strings.delete_repo_confirmation, dialog.repo),
) )
} }
} }

View File

@ -1,10 +1,12 @@
package eu.kanade.tachiyomi.ui.category.repos package eu.kanade.presentation.category.repos
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.screenModelScope import cafe.adriel.voyager.core.model.screenModelScope
import dev.icerock.moko.resources.StringResource import dev.icerock.moko.resources.StringResource
import eu.kanade.domain.source.interactor.CreateSourceRepo import eu.kanade.domain.source.interactor.CreateSourceRepo
import eu.kanade.domain.source.interactor.DeleteSourceRepos
import eu.kanade.domain.source.interactor.GetSourceRepos
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
@ -12,10 +14,7 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import tachiyomi.core.util.lang.launchIO import tachiyomi.core.util.lang.launchIO
import tachiyomi.domain.source.interactor.DeleteSourceRepos
import tachiyomi.domain.source.interactor.GetSourceRepos
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.i18n.sy.SYMR
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@ -87,7 +86,7 @@ class RepoScreenModel(
sealed class RepoEvent { sealed class RepoEvent {
sealed class LocalizedMessage(val stringRes: StringResource) : RepoEvent() sealed class LocalizedMessage(val stringRes: StringResource) : RepoEvent()
data object InvalidName : LocalizedMessage(SYMR.strings.invalid_repo_name) data object InvalidName : LocalizedMessage(MR.strings.invalid_repo_name)
data object InternalError : LocalizedMessage(MR.strings.internal_error) data object InternalError : LocalizedMessage(MR.strings.internal_error)
} }

View File

@ -10,8 +10,8 @@ import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.domain.ui.UiPreferences import eu.kanade.domain.ui.UiPreferences
import eu.kanade.presentation.category.repos.RepoScreen
import eu.kanade.presentation.more.settings.Preference import eu.kanade.presentation.more.settings.Preference
import eu.kanade.tachiyomi.ui.category.repos.RepoScreen
import eu.kanade.tachiyomi.ui.category.sources.SourceCategoryScreen import eu.kanade.tachiyomi.ui.category.sources.SourceCategoryScreen
import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.authenticate import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.authenticate
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
@ -34,7 +34,11 @@ object SettingsBrowseScreen : SearchableSettings {
@Composable @Composable
override fun getPreferences(): List<Preference> { override fun getPreferences(): List<Preference> {
val context = LocalContext.current val context = LocalContext.current
val navigator = LocalNavigator.currentOrThrow
val sourcePreferences = remember { Injekt.get<SourcePreferences>() } val sourcePreferences = remember { Injekt.get<SourcePreferences>() }
val reposCount by sourcePreferences.extensionRepos().collectAsState()
// SY --> // SY -->
val uiPreferences = remember { Injekt.get<UiPreferences>() } val uiPreferences = remember { Injekt.get<UiPreferences>() }
val unsortedPreferences = remember { Injekt.get<UnsortedPreferences>() } val unsortedPreferences = remember { Injekt.get<UnsortedPreferences>() }
@ -45,7 +49,6 @@ object SettingsBrowseScreen : SearchableSettings {
title = stringResource(MR.strings.label_sources), title = stringResource(MR.strings.label_sources),
preferenceItems = persistentListOf( preferenceItems = persistentListOf(
kotlin.run { kotlin.run {
val navigator = LocalNavigator.currentOrThrow
val count by sourcePreferences.sourcesTabCategories().collectAsState() val count by sourcePreferences.sourcesTabCategories().collectAsState()
Preference.PreferenceItem.TextPreference( Preference.PreferenceItem.TextPreference(
title = stringResource(MR.strings.action_edit_categories), title = stringResource(MR.strings.action_edit_categories),
@ -82,22 +85,6 @@ object SettingsBrowseScreen : SearchableSettings {
), ),
), ),
), ),
Preference.PreferenceGroup(
title = stringResource(MR.strings.label_extensions),
preferenceItems = persistentListOf(
kotlin.run {
val navigator = LocalNavigator.currentOrThrow
val count by unsortedPreferences.extensionRepos().collectAsState()
Preference.PreferenceItem.TextPreference(
title = stringResource(SYMR.strings.action_edit_repos),
subtitle = pluralStringResource(SYMR.plurals.num_repos, count.size, count.size),
onClick = {
navigator.push(RepoScreen())
},
)
},
),
),
// SY <-- // SY <--
Preference.PreferenceGroup( Preference.PreferenceGroup(
title = stringResource(MR.strings.label_sources), title = stringResource(MR.strings.label_sources),
@ -106,6 +93,13 @@ object SettingsBrowseScreen : SearchableSettings {
pref = sourcePreferences.hideInLibraryItems(), pref = sourcePreferences.hideInLibraryItems(),
title = stringResource(MR.strings.pref_hide_in_library_items), title = stringResource(MR.strings.pref_hide_in_library_items),
), ),
Preference.PreferenceItem.TextPreference(
title = stringResource(MR.strings.label_extension_repos),
subtitle = pluralStringResource(MR.plurals.num_repos, reposCount.size, reposCount.size),
onClick = {
navigator.push(RepoScreen())
},
),
), ),
), ),
Preference.PreferenceGroup( Preference.PreferenceGroup(

View File

@ -18,7 +18,6 @@ import tachiyomi.core.preference.Preference
import tachiyomi.core.preference.PreferenceStore import tachiyomi.core.preference.PreferenceStore
import tachiyomi.core.util.lang.withIOContext import tachiyomi.core.util.lang.withIOContext
import tachiyomi.core.util.system.logcat import tachiyomi.core.util.system.logcat
import tachiyomi.domain.UnsortedPreferences
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.time.Instant import java.time.Instant
import kotlin.time.Duration.Companion.days import kotlin.time.Duration.Companion.days
@ -27,6 +26,7 @@ internal class ExtensionGithubApi {
private val networkService: NetworkHelper by injectLazy() private val networkService: NetworkHelper by injectLazy()
private val preferenceStore: PreferenceStore by injectLazy() private val preferenceStore: PreferenceStore by injectLazy()
private val sourcePreferences: SourcePreferences by injectLazy()
private val extensionManager: ExtensionManager by injectLazy() private val extensionManager: ExtensionManager by injectLazy()
private val json: Json by injectLazy() private val json: Json by injectLazy()
@ -34,11 +34,6 @@ internal class ExtensionGithubApi {
preferenceStore.getLong(Preference.appStateKey("last_ext_check"), 0) preferenceStore.getLong(Preference.appStateKey("last_ext_check"), 0)
} }
// SY -->
private val sourcePreferences: SourcePreferences by injectLazy()
private val unsortedPreferences: UnsortedPreferences by injectLazy()
// SY <--
private var requiresFallbackSource = false private var requiresFallbackSource = false
suspend fun findExtensions(): List<Extension.Available> { suspend fun findExtensions(): List<Extension.Available> {
@ -66,7 +61,7 @@ internal class ExtensionGithubApi {
val extensions = with(json) { val extensions = with(json) {
response response
.parseAs<List<ExtensionJsonObject>>() .parseAs<List<ExtensionJsonObject>>()
.toExtensions() /* SY --> */ + unsortedPreferences.extensionRepos() .toExtensions() + sourcePreferences.extensionRepos()
.get() .get()
.flatMap { repoPath -> .flatMap { repoPath ->
val url = if (requiresFallbackSource) { val url = if (requiresFallbackSource) {
@ -80,7 +75,6 @@ internal class ExtensionGithubApi {
.parseAs<List<ExtensionJsonObject>>() .parseAs<List<ExtensionJsonObject>>()
.toExtensions(url, repoSource = true) .toExtensions(url, repoSource = true)
} }
// SY <--
} }
// Sanity check - a small number of extensions probably means something broke // Sanity check - a small number of extensions probably means something broke
@ -138,10 +132,8 @@ internal class ExtensionGithubApi {
} }
private fun List<ExtensionJsonObject>.toExtensions( private fun List<ExtensionJsonObject>.toExtensions(
// SY -->
repoUrl: String = getUrlPrefix(), repoUrl: String = getUrlPrefix(),
repoSource: Boolean = false, repoSource: Boolean = false,
// SY <--
): List<Extension.Available> { ): List<Extension.Available> {
return this return this
.filter { .filter {
@ -159,17 +151,15 @@ internal class ExtensionGithubApi {
isNsfw = it.nsfw == 1, isNsfw = it.nsfw == 1,
sources = it.sources?.map(extensionSourceMapper).orEmpty(), sources = it.sources?.map(extensionSourceMapper).orEmpty(),
apkName = it.apk, apkName = it.apk,
iconUrl = "${/* SY --> */ repoUrl /* SY <-- */}icon/${it.pkg}.png", iconUrl = "${repoUrl}icon/${it.pkg}.png",
// SY -->
repoUrl = repoUrl, repoUrl = repoUrl,
isRepoSource = repoSource, isRepoSource = repoSource,
// SY <--
) )
} }
} }
fun getApkUrl(extension: Extension.Available): String { fun getApkUrl(extension: Extension.Available): String {
return /* SY --> */ "${extension.repoUrl}/apk/${extension.apkName}" // SY <-- return "${extension.repoUrl}/apk/${extension.apkName}"
} }
private fun getUrlPrefix(): String { private fun getUrlPrefix(): String {

View File

@ -29,10 +29,10 @@ sealed class Extension {
val isObsolete: Boolean = false, val isObsolete: Boolean = false,
val isUnofficial: Boolean = false, val isUnofficial: Boolean = false,
val isShared: Boolean, val isShared: Boolean,
// SY -->
val isRedundant: Boolean = false,
val repoUrl: String? = null, val repoUrl: String? = null,
val isRepoSource: Boolean = false, val isRepoSource: Boolean = false,
// SY -->
val isRedundant: Boolean = false,
// SY <-- // SY <--
) : Extension() ) : Extension()
@ -47,10 +47,8 @@ sealed class Extension {
val sources: List<Source>, val sources: List<Source>,
val apkName: String, val apkName: String,
val iconUrl: String, val iconUrl: String,
// SY -->
val repoUrl: String, val repoUrl: String,
val isRepoSource: Boolean, val isRepoSource: Boolean,
// SY <--
) : Extension() { ) : Extension() {
data class Source( data class Source(

View File

@ -371,7 +371,10 @@ open class BrowseSourceScreenModel(
else -> { else -> {
val preselectedIds = getCategories.await(manga.id).map { it.id } val preselectedIds = getCategories.await(manga.id).map { it.id }
setDialog( setDialog(
Dialog.ChangeMangaCategory(manga, categories.mapAsCheckboxState { it.id in preselectedIds }.toImmutableList()), Dialog.ChangeMangaCategory(
manga,
categories.mapAsCheckboxState { it.id in preselectedIds }.toImmutableList(),
),
) )
} }
} }

View File

@ -56,30 +56,24 @@ class CategoryScreen : Screen() {
CategoryCreateDialog( CategoryCreateDialog(
onDismissRequest = screenModel::dismissDialog, onDismissRequest = screenModel::dismissDialog,
onCreate = screenModel::createCategory, onCreate = screenModel::createCategory,
// SY -->
categories = successState.categories.fastMap { it.name }.toImmutableList(), categories = successState.categories.fastMap { it.name }.toImmutableList(),
title = stringResource(MR.strings.action_add_category), title = stringResource(MR.strings.action_add_category),
// SY <--
) )
} }
is CategoryDialog.Rename -> { is CategoryDialog.Rename -> {
CategoryRenameDialog( CategoryRenameDialog(
onDismissRequest = screenModel::dismissDialog, onDismissRequest = screenModel::dismissDialog,
onRename = { screenModel.renameCategory(dialog.category, it) }, onRename = { screenModel.renameCategory(dialog.category, it) },
// SY -->
categories = successState.categories.fastMap { it.name }.toImmutableList(), categories = successState.categories.fastMap { it.name }.toImmutableList(),
category = dialog.category.name, category = dialog.category.name,
// SY <--
) )
} }
is CategoryDialog.Delete -> { is CategoryDialog.Delete -> {
CategoryDeleteDialog( CategoryDeleteDialog(
onDismissRequest = screenModel::dismissDialog, onDismissRequest = screenModel::dismissDialog,
onDelete = { screenModel.deleteCategory(dialog.category.id) }, onDelete = { screenModel.deleteCategory(dialog.category.id) },
// SY -->
title = stringResource(MR.strings.delete_category), title = stringResource(MR.strings.delete_category),
text = stringResource(MR.strings.delete_category_confirmation, dialog.category.name), text = stringResource(MR.strings.delete_category_confirmation, dialog.category.name),
// SY <--
) )
} }
is CategoryDialog.SortAlphabetically -> { is CategoryDialog.SortAlphabetically -> {

View File

@ -1117,13 +1117,15 @@ class LibraryScreenModel(
val common = getCommonCategories(mangaList) val common = getCommonCategories(mangaList)
// Get indexes of the mix categories to preselect. // Get indexes of the mix categories to preselect.
val mix = getMixCategories(mangaList) val mix = getMixCategories(mangaList)
val preselected = categories.map { val preselected = categories
when (it) { .map {
in common -> CheckboxState.State.Checked(it) when (it) {
in mix -> CheckboxState.TriState.Exclude(it) in common -> CheckboxState.State.Checked(it)
else -> CheckboxState.State.None(it) in mix -> CheckboxState.TriState.Exclude(it)
else -> CheckboxState.State.None(it)
}
} }
}.toImmutableList() .toImmutableList()
mutableState.update { it.copy(dialog = Dialog.ChangeCategory(mangaList, preselected)) } mutableState.update { it.copy(dialog = Dialog.ChangeCategory(mangaList, preselected)) }
} }
} }

View File

@ -121,6 +121,7 @@ class ChapterLoader(
source = source, source = source,
downloadManager = downloadManager, downloadManager = downloadManager,
downloadProvider = downloadProvider, downloadProvider = downloadProvider,
tempFileManager = tempFileManager,
) )
source is HttpSource -> HttpPageLoader(chapter, source) source is HttpSource -> HttpPageLoader(chapter, source)
source is LocalSource -> source.getFormat(chapter.chapter).let { format -> source is LocalSource -> source.getFormat(chapter.chapter).let { format ->
@ -139,7 +140,7 @@ class ChapterLoader(
} }
} }
// SY <-- // SY <--
isDownloaded -> DownloadPageLoader(chapter, manga, source, downloadManager, downloadProvider) isDownloaded -> DownloadPageLoader(chapter, manga, source, downloadManager, downloadProvider, tempFileManager)
source is LocalSource -> source.getFormat(chapter.chapter).let { format -> source is LocalSource -> source.getFormat(chapter.chapter).let { format ->
when (format) { when (format) {
is Format.Directory -> DirectoryPageLoader(format.file) is Format.Directory -> DirectoryPageLoader(format.file)

View File

@ -18,10 +18,6 @@
<item quantity="one">%1$d tag in sorting list. This adds a option in the library to sort by a priority based tag list, which means entries will be sorted in a way to prioritise the ones with the tags you want</item> <item quantity="one">%1$d tag in sorting list. This adds a option in the library to sort by a priority based tag list, which means entries will be sorted in a way to prioritise the ones with the tags you want</item>
<item quantity="other">%1$d tags in sorting list. This adds a option in the library to sort by a priority based tag list, which means entries will be sorted in a way to prioritise the ones with the tags you want</item> <item quantity="other">%1$d tags in sorting list. This adds a option in the library to sort by a priority based tag list, which means entries will be sorted in a way to prioritise the ones with the tags you want</item>
</plurals> </plurals>
<plurals name="num_repos">
<item quantity="one">%d additional repo</item>
<item quantity="other">%d additional repos</item>
</plurals>
<plurals name="migrate_entry"> <plurals name="migrate_entry">
<item quantity="one">Migrate %1$d%2$s entry?</item> <item quantity="one">Migrate %1$d%2$s entry?</item>
<item quantity="other">Migrate %1$d%2$s entries?</item> <item quantity="other">Migrate %1$d%2$s entries?</item>

View File

@ -391,18 +391,6 @@
<!-- Extension section --> <!-- Extension section -->
<string name="ext_redundant">Redundant</string> <string name="ext_redundant">Redundant</string>
<string name="redundant_extension_message">This extension is redundant and will not be used inside this version of Tachiyomi.</string> <string name="redundant_extension_message">This extension is redundant and will not be used inside this version of Tachiyomi.</string>
<string name="repo_extension_message">This extension is from an external repo. Tap to view the repo.</string>
<!-- Extension Repos -->
<string name="information_empty_repos">You have no additional repos. Tap the plus button to create one for adding external extensions.</string>
<string name="action_add_repo">Add repo</string>
<string name="action_add_repo_message">Add additional repos to Tachiyomi, the format of a repo is \'username/repo\' , with username being the repo owner, and repo being the repo name</string>
<string name="action_edit_repos">Edit repos</string>
<string name="error_repo_exists">This repo already exists!</string>
<string name="invalid_repo_name">Invalid repo name</string>
<string name="repo_source">Repo source</string>
<string name="delete_repo">Delete repo</string>
<string name="delete_repo_confirmation">Do you wish to delete the repo %s?</string>
<!-- Migration --> <!-- Migration -->
<string name="select_sources">Select sources</string> <string name="select_sources">Select sources</string>

View File

@ -18,10 +18,6 @@
<item quantity="one">Ada %1$d tagar dalam daftar penyortiran. Ini menambahkan opsi di perpustakaan untuk mengurutkan berdasarkan daftar tagar berdasarkan prioritas, yang berarti manga akan diurutkan dengan cara memprioritaskan sesuai dengan tagar yang Anda inginkan</item> <item quantity="one">Ada %1$d tagar dalam daftar penyortiran. Ini menambahkan opsi di perpustakaan untuk mengurutkan berdasarkan daftar tagar berdasarkan prioritas, yang berarti manga akan diurutkan dengan cara memprioritaskan sesuai dengan tagar yang Anda inginkan</item>
<item quantity="other">Ada %1$d tagar dalam daftar penyortiran. Ini menambahkan opsi di perpustakaan untuk mengurutkan berdasarkan daftar tagar berdasarkan prioritas, yang berarti manga akan diurutkan dengan cara memprioritaskan sesuai dengan tagar yang Anda inginkan</item> <item quantity="other">Ada %1$d tagar dalam daftar penyortiran. Ini menambahkan opsi di perpustakaan untuk mengurutkan berdasarkan daftar tagar berdasarkan prioritas, yang berarti manga akan diurutkan dengan cara memprioritaskan sesuai dengan tagar yang Anda inginkan</item>
</plurals> </plurals>
<plurals name="num_repos">
<item quantity="one">%d repo tambahan</item>
<item quantity="other">%d repo tambahan</item>
</plurals>
<plurals name="migrate_entry"> <plurals name="migrate_entry">
<item quantity="one">Pindahkan %1$d%2$s manga?</item> <item quantity="one">Pindahkan %1$d%2$s manga?</item>
<item quantity="other">Pindahkan %1$d%2$s manga?</item> <item quantity="other">Pindahkan %1$d%2$s manga?</item>

View File

@ -336,16 +336,6 @@
<string name="ext_redundant">Duplikat</string> <string name="ext_redundant">Duplikat</string>
<string name="redundant_extension_message">Ekstensi ini duplikat dan tak akan digunakan di dalam versi Tachiyomi ini.</string> <string name="redundant_extension_message">Ekstensi ini duplikat dan tak akan digunakan di dalam versi Tachiyomi ini.</string>
<!-- Extension Repos -->
<string name="information_empty_repos">Kamu tidak memiliki repo tambahan. Ketuk tombol tambah untuk menambahkan ekstensi eksternal.</string>
<string name="action_add_repo">Tambah repo</string>
<string name="action_add_repo_message">Tambahkan repo tambahan ke Tachiyomi, format repo adalah \'username/repo\' , username diisi dengan pemilik repo, dan repo diisi dengan nama repo</string>
<string name="action_edit_repos">Edit repo</string>
<string name="error_repo_exists">Repo ini sudah ada!</string>
<string name="invalid_repo_name">Nama repo tidak valid</string>
<string name="repo_source">Sumber Repo</string>
<!-- Migration --> <!-- Migration -->
<string name="select_sources">Pilih sumber</string> <string name="select_sources">Pilih sumber</string>
<string name="select_none">Tak ada yang dipilih</string> <string name="select_none">Tak ada yang dipilih</string>

View File

@ -13,10 +13,6 @@
<item quantity="one">%1$d tag na lista de ordenação. Isto dá uma opção na biblioteca de ordenar por uma lista de tags baseada em prioridade, ou seja, os mangás serão ordenados de modo a priorizar aqueles com as tags que deseja</item> <item quantity="one">%1$d tag na lista de ordenação. Isto dá uma opção na biblioteca de ordenar por uma lista de tags baseada em prioridade, ou seja, os mangás serão ordenados de modo a priorizar aqueles com as tags que deseja</item>
<item quantity="other">%1$d tags na lista de ordenação. Isto dá uma opção na biblioteca de ordenar por uma lista de tags baseada em prioridade, ou seja, os mangás serão ordenados de modo a priorizar aqueles com as tags que deseja</item> <item quantity="other">%1$d tags na lista de ordenação. Isto dá uma opção na biblioteca de ordenar por uma lista de tags baseada em prioridade, ou seja, os mangás serão ordenados de modo a priorizar aqueles com as tags que deseja</item>
</plurals> </plurals>
<plurals name="num_repos">
<item quantity="one">%d repositório adicional</item>
<item quantity="other">%d repositórios adicionais</item>
</plurals>
<plurals name="migrate_entry"> <plurals name="migrate_entry">
<item quantity="one">Migrar %1$d%2$s mangá?</item> <item quantity="one">Migrar %1$d%2$s mangá?</item>

View File

@ -312,15 +312,6 @@
<string name="ext_redundant">Redundante</string> <string name="ext_redundant">Redundante</string>
<string name="redundant_extension_message">Esta extensão é redundante e não será usada nesta versão do Tachiyomi.</string> <string name="redundant_extension_message">Esta extensão é redundante e não será usada nesta versão do Tachiyomi.</string>
<!-- Extension Repos -->
<string name="information_empty_repos">Você não tem repositórios adicionais. Toque no botão Adicionar para criar um e adicionar extensões externas.</string>
<string name="action_add_repo">Adicionar repositório</string>
<string name="action_add_repo_message">Adicione repositórios adicionais ao Tachiyomi, o formato de um repositório é \'username/repo\', sendo username o dono do repositório, e repo o nome do repositório</string>
<string name="action_edit_repos">Editar repositórios</string>
<string name="error_repo_exists">Este repositório já existe!</string>
<string name="invalid_repo_name">Nome de repositório inválido</string>
<string name="repo_source">Fonte do repositório</string>
<!-- Migration --> <!-- Migration -->
<string name="select_sources">Selecionar fontes</string> <string name="select_sources">Selecionar fontes</string>
<string name="select_none">Selecionar nenhum</string> <string name="select_none">Selecionar nenhum</string>

View File

@ -24,12 +24,7 @@
<item quantity="many">%1$d тэгов в списке сортировки. Это добавляет в библиотеку возможность сортировки по тэгам, основанному на приоритете. Это означает, что будут отображатся только те серии, которые имеют добавленные тэги.</item> <item quantity="many">%1$d тэгов в списке сортировки. Это добавляет в библиотеку возможность сортировки по тэгам, основанному на приоритете. Это означает, что будут отображатся только те серии, которые имеют добавленные тэги.</item>
<item quantity="other">%1$d тэгов в списке сортировки. Это добавляет в библиотеку возможность сортировки по тэгам, основанному на приоритете. Это означает, что будут отображатся только те серии, которые имеют добавленные тэги.</item> <item quantity="other">%1$d тэгов в списке сортировки. Это добавляет в библиотеку возможность сортировки по тэгам, основанному на приоритете. Это означает, что будут отображатся только те серии, которые имеют добавленные тэги.</item>
</plurals> </plurals>
<plurals name="num_repos">
<item quantity="one">%d дополнительный репозиторий</item>
<item quantity="few">%d дополнительных репозитория</item>
<item quantity="many">%d дополнительных репозиториев</item>
<item quantity="other">%d дополнительных репозиториев</item>
</plurals>
<plurals name="migrate_entry"> <plurals name="migrate_entry">
<item quantity="one">Перенести %1$d%2$s серию?</item> <item quantity="one">Перенести %1$d%2$s серию?</item>
<item quantity="few">Перенести %1$d%2$s серии?</item> <item quantity="few">Перенести %1$d%2$s серии?</item>

View File

@ -380,18 +380,6 @@
<!-- Extension section --> <!-- Extension section -->
<string name="ext_redundant">Избыточное</string> <string name="ext_redundant">Избыточное</string>
<string name="redundant_extension_message">Это расширение является избыточным и не будет использоваться внутри этой версии Tachiyomi.</string> <string name="redundant_extension_message">Это расширение является избыточным и не будет использоваться внутри этой версии Tachiyomi.</string>
<string name="repo_extension_message">Это расширение из внешнего репозитория. Нажмите, чтобы просмотреть репозиторий.</string>
<!-- Extension Repos -->
<string name="information_empty_repos">У вас нет дополнительных репозиториев. Нажмите кнопку «Добавить» чтобы создать репозиторий для добавления внешних расширений.</string>
<string name="action_add_repo">Добавить репозиторий</string>
<string name="action_add_repo_message">Добавить дополнительные репозиторий в TachiyomiSY (Формат username/repo). Username - является владельцем репозитория, а repo - имя репозитория</string>
<string name="action_edit_repos">Редактировать репозитории</string>
<string name="error_repo_exists">Этот репозиторий уже существует!</string>
<string name="invalid_repo_name">Недопустимое имя репозитория</string>
<string name="repo_source">Источник репозитория</string>
<string name="delete_repo">Удалить репозиторий</string>
<string name="delete_repo_confirmation">Хотите ли вы удалить репозиторий %s?</string>
<!-- Migration --> <!-- Migration -->
<string name="select_sources">Выберите источники</string> <string name="select_sources">Выберите источники</string>

View File

@ -12,9 +12,6 @@
<plurals name="pref_tag_sorting_desc"> <plurals name="pref_tag_sorting_desc">
<item quantity="other">%1$d 个标签在排序列表中。 这在书架中增加了一个选项,以基于优先级的标签列表进行排序,这意味着作品将以你想要的标签优先的方式进行排序。</item> <item quantity="other">%1$d 个标签在排序列表中。 这在书架中增加了一个选项,以基于优先级的标签列表进行排序,这意味着作品将以你想要的标签优先的方式进行排序。</item>
</plurals> </plurals>
<plurals name="num_repos">
<item quantity="other">%d 个额外的仓库</item>
</plurals>
<plurals name="migrate_entry"> <plurals name="migrate_entry">
<item quantity="other">迁移 %1$d%2$s 作品?</item> <item quantity="other">迁移 %1$d%2$s 作品?</item>

View File

@ -376,18 +376,6 @@
<!-- Extension section --> <!-- Extension section -->
<string name="ext_redundant">废弃</string> <string name="ext_redundant">废弃</string>
<string name="redundant_extension_message">此插件已废弃,将不会在此版本的 Tachiyomi 上工作。</string> <string name="redundant_extension_message">此插件已废弃,将不会在此版本的 Tachiyomi 上工作。</string>
<string name="repo_extension_message">此插件来自外部仓库。点击以浏览其仓库。</string>
<!-- Extension Repos -->
<string name="information_empty_repos">你没有额外的仓库。点击加号按钮以创建一个用于添加外部插件。</string>
<string name="action_add_repo">添加仓库</string>
<string name="action_add_repo_message">在 Tachiyomi 中添加额外的仓库,仓库的格式是 \'username/repo\'username是仓库所有者repo是仓库名称。</string>
<string name="action_edit_repos">编辑仓库</string>
<string name="error_repo_exists">此仓库已存在!</string>
<string name="invalid_repo_name">无效的仓库名称</string>
<string name="repo_source">仓库来源</string>
<string name="delete_repo">删除仓库</string>
<string name="delete_repo_confirmation">你想要删除仓库 %s 吗?</string>
<!-- Migration --> <!-- Migration -->
<string name="select_sources">选择来源</string> <string name="select_sources">选择来源</string>

View File

@ -12,9 +12,6 @@
<plurals name="pref_tag_sorting_desc"> <plurals name="pref_tag_sorting_desc">
<item quantity="other">%1$d 個標籤在排序列表中。 這在書架中增加了一個選項,以基於優先級的標籤列表進行排序,這意味著作品將以你想要的標籤優先的方式進行排序。</item> <item quantity="other">%1$d 個標籤在排序列表中。 這在書架中增加了一個選項,以基於優先級的標籤列表進行排序,這意味著作品將以你想要的標籤優先的方式進行排序。</item>
</plurals> </plurals>
<plurals name="num_repos">
<item quantity="other">%d 個額外的倉庫</item>
</plurals>
<plurals name="migrate_entry"> <plurals name="migrate_entry">
<item quantity="other">遷移 %1$d%2$s 作品?</item> <item quantity="other">遷移 %1$d%2$s 作品?</item>

View File

@ -376,18 +376,6 @@
<!-- Extension section --> <!-- Extension section -->
<string name="ext_redundant">廢棄</string> <string name="ext_redundant">廢棄</string>
<string name="redundant_extension_message">此插件已廢棄,將不會在此版本的 Tachiyomi 上工作。</string> <string name="redundant_extension_message">此插件已廢棄,將不會在此版本的 Tachiyomi 上工作。</string>
<string name="repo_extension_message">此插件來自外部倉庫。點擊以瀏覽其倉庫。</string>
<!-- Extension Repos -->
<string name="information_empty_repos">你沒有額外的倉庫。點擊加號按鈕以創建一個用於新增外部插件。</string>
<string name="action_add_repo">新增倉庫</string>
<string name="action_add_repo_message">在 Tachiyomi 中新增額外的倉庫,倉庫的格式是 \'username/repo\'username是倉庫所有者repo是倉庫名稱。</string>
<string name="action_edit_repos">編輯倉庫</string>
<string name="error_repo_exists">此倉庫已存在!</string>
<string name="invalid_repo_name">無效的倉庫名稱</string>
<string name="repo_source">倉庫來源</string>
<string name="delete_repo">刪除倉庫</string>
<string name="delete_repo_confirmation">你想要刪除倉庫 %s 嗎?</string>
<!-- Migration --> <!-- Migration -->
<string name="select_sources">選擇來源</string> <string name="select_sources">選擇來源</string>

View File

@ -80,4 +80,9 @@
<item quantity="one">Extension update available</item> <item quantity="one">Extension update available</item>
<item quantity="other">%d extension updates available</item> <item quantity="other">%d extension updates available</item>
</plurals> </plurals>
<plurals name="num_repos">
<item quantity="one">%d repo</item>
<item quantity="other">%d repos</item>
</plurals>
</resources> </resources>

View File

@ -336,6 +336,17 @@
<string name="ext_installer_shizuku_stopped">Shizuku is not running</string> <string name="ext_installer_shizuku_stopped">Shizuku is not running</string>
<string name="ext_installer_shizuku_unavailable_dialog">Install and start Shizuku to use Shizuku as extension installer.</string> <string name="ext_installer_shizuku_unavailable_dialog">Install and start Shizuku to use Shizuku as extension installer.</string>
<!-- Extension repos -->
<string name="label_extension_repos">Extension repos</string>
<string name="information_empty_repos">You have no repos set.</string>
<string name="action_add_repo">Add repo</string>
<string name="action_add_repo_message">Add additional repos to Tachiyomi, the format of a repo is \"username/repo\", with username being the repo owner, and repo being the repo name.</string>
<string name="error_repo_exists">This repo already exists!</string>
<string name="action_delete_repo">Delete repo</string>
<string name="invalid_repo_name">Invalid repo name</string>
<string name="delete_repo_confirmation">Do you wish to delete the repo \"%s\"?</string>
<string name="repo_extension_message">This extension is from an external repo. Tap to view the repo.</string>
<!-- Reader section --> <!-- Reader section -->
<string name="pref_fullscreen">Fullscreen</string> <string name="pref_fullscreen">Fullscreen</string>
<string name="pref_show_navigation_mode">Show tap zones overlay</string> <string name="pref_show_navigation_mode">Show tap zones overlay</string>