From 2536e5a77e094c96bab51275e2abe91b25717be5 Mon Sep 17 00:00:00 2001 From: "Tran M. Cuong" Date: Sat, 24 Aug 2024 22:30:13 +0700 Subject: [PATCH] Remove built-in Komikku repo (#286) * Add Help to Repo EmptyScreen * Migration: insert Official Repo for old users * button to auto fill official KMK extension repo * 'More extensions' button to go to Repo page * Remove default official repo * fix spotless --- .../source/service/SourcePreferences.kt | 1 + .../presentation/browse/ExtensionsScreen.kt | 42 ++++++++++---- .../components/ExtensionReposDialogs.kt | 57 +++++++++++++++---- .../browse/components/ExtensionReposScreen.kt | 37 ++++++++++++ .../tachiyomi/extension/api/ExtensionApi.kt | 21 ++----- .../browse/extension/ExtensionsScreenModel.kt | 6 ++ .../java/mihon/core/migration/Migration.kt | 1 + .../core/migration/migrations/Migrations.kt | 3 + .../OfficialExtensionRepositoryMigration.kt | 32 +++++++++++ .../interactor/CreateExtensionRepo.kt | 3 +- .../moko-resources/base/strings.xml | 1 + .../presentation/core/screens/EmptyScreen.kt | 5 ++ 12 files changed, 168 insertions(+), 41 deletions(-) create mode 100644 app/src/main/java/mihon/core/migration/migrations/OfficialExtensionRepositoryMigration.kt diff --git a/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt b/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt index 1864081b7..dae2b0ac2 100644 --- a/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt +++ b/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt @@ -40,6 +40,7 @@ class SourcePreferences( fun hideInLibraryItems() = preferenceStore.getBoolean("browse_hide_in_library_items", false) + @Deprecated("Use ExtensionRepoRepository instead", replaceWith = ReplaceWith("ExtensionRepoRepository.getAll()")) fun extensionRepos() = preferenceStore.getStringSet("extension_repos", emptySet()) fun extensionUpdatesCount() = preferenceStore.getInt("ext_updates_count", 0) diff --git a/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt index cd3f7f1b4..2c223997f 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt @@ -157,6 +157,9 @@ private fun ExtensionContent( val context = LocalContext.current var trustState by remember { mutableStateOf(null) } val installGranted = rememberRequestPackageInstallsPermissionState(initialValue = true) + // KMK --> + val navigator = LocalNavigator.current + // KMK <-- FastScrollLazyColumn( contentPadding = contentPadding + topSmallPaddingValues, @@ -180,19 +183,36 @@ private fun ExtensionContent( when (header) { is ExtensionUiModel.Header.Resource -> { val action: @Composable RowScope.() -> Unit = - if (header.textRes == MR.strings.ext_updates_pending) { - { - Button(onClick = { onClickUpdateAll() }) { - Text( - text = stringResource(MR.strings.ext_update_all), - style = LocalTextStyle.current.copy( - color = MaterialTheme.colorScheme.onPrimary, - ), - ) + when (header.textRes) { + MR.strings.ext_updates_pending -> { + { + Button(onClick = { onClickUpdateAll() }) { + Text( + text = stringResource(MR.strings.ext_update_all), + style = LocalTextStyle.current.copy( + color = MaterialTheme.colorScheme.onPrimary, + ), + ) + } } } - } else { - {} + // KMK --> + KMR.strings.extensions_page_more -> { + { + Button(onClick = { navigator?.push(ExtensionReposScreen()) }) { + Text( + text = stringResource(MR.strings.action_add_repo), + style = LocalTextStyle.current.copy( + color = MaterialTheme.colorScheme.onPrimary, + ), + ) + } + } + } + // KMK <-- + else -> { + {} + } } ExtensionHeader( textRes = header.textRes, diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposDialogs.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposDialogs.kt index 239f917d0..1f6d7cc56 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposDialogs.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposDialogs.kt @@ -1,6 +1,9 @@ package eu.kanade.presentation.more.settings.screen.browse.components +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.AlertDialog import androidx.compose.material3.OutlinedTextField @@ -12,12 +15,16 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.tooling.preview.Preview import kotlinx.collections.immutable.ImmutableSet +import kotlinx.collections.immutable.persistentSetOf import kotlinx.coroutines.delay +import mihon.domain.extensionrepo.interactor.CreateExtensionRepo.Companion.OFFICIAL_REPO_BASE_URL import mihon.domain.extensionrepo.model.ExtensionRepo import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.stringResource @@ -37,19 +44,37 @@ fun ExtensionRepoCreateDialog( AlertDialog( onDismissRequest = onDismissRequest, confirmButton = { - TextButton( - enabled = name.isNotEmpty() && !nameAlreadyExists, - onClick = { - onCreate(name) - onDismissRequest() - }, + // KMK --> + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, // Distribute space between items + verticalAlignment = Alignment.CenterVertically, // Align items vertically ) { - Text(text = stringResource(MR.strings.action_add)) - } - }, - dismissButton = { - TextButton(onClick = onDismissRequest) { - Text(text = stringResource(MR.strings.action_cancel)) + TextButton( + onClick = { + name = "$OFFICIAL_REPO_BASE_URL/index.min.json" + }, + ) { + Text(text = stringResource(MR.strings.label_default)) + } + Row( + // Group the right-aligned elements + verticalAlignment = Alignment.CenterVertically, + ) { + TextButton(onClick = onDismissRequest) { + Text(text = stringResource(MR.strings.action_cancel)) + } + // KMK <-- + TextButton( + enabled = name.isNotEmpty() && !nameAlreadyExists, + onClick = { + onCreate(name) + onDismissRequest() + }, + ) { + Text(text = stringResource(MR.strings.action_add)) + } + } } }, title = { @@ -90,6 +115,14 @@ fun ExtensionRepoCreateDialog( } } +// KMK --> +@Preview(showBackground = true) +@Composable +fun ExtensionRepoCreateDialogPreview() { + ExtensionRepoCreateDialog({ }, { }, persistentSetOf()) +} +// KMK <-- + @Composable fun ExtensionRepoDeleteDialog( onDismissRequest: () -> Unit, diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposScreen.kt index b07ba4101..37183a364 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposScreen.kt @@ -3,18 +3,28 @@ package eu.kanade.presentation.more.settings.screen.browse.components import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.outlined.Help import androidx.compose.material.icons.outlined.Refresh import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.tooling.preview.Preview import eu.kanade.presentation.category.components.CategoryFloatingActionButton import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.more.settings.screen.browse.RepoScreenState +import eu.kanade.tachiyomi.util.system.openInBrowser +import kotlinx.collections.immutable.persistentSetOf +import mihon.domain.extensionrepo.interactor.CreateExtensionRepo.Companion.OFFICIAL_REPO_WEBSITE import mihon.domain.extensionrepo.model.ExtensionRepo import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.Scaffold @@ -58,9 +68,20 @@ fun ExtensionReposScreen( }, ) { paddingValues -> if (state.isEmpty) { + val context = LocalContext.current EmptyScreen( MR.strings.information_empty_repos, modifier = Modifier.padding(paddingValues), + help = { + TextButton( + onClick = { context.openInBrowser(OFFICIAL_REPO_WEBSITE) }, + modifier = Modifier.padding(top = MaterialTheme.padding.small), + ) { + Icon(imageVector = Icons.AutoMirrored.Outlined.Help, contentDescription = null) + Spacer(modifier = Modifier.width(MaterialTheme.padding.extraSmall)) + Text(text = stringResource(MR.strings.label_help)) + } + }, ) return@Scaffold } @@ -75,3 +96,19 @@ fun ExtensionReposScreen( ) } } + +// KMK --> +@Preview +@Composable +private fun ExtensionReposScreenPreview() { + val state = RepoScreenState.Success(repos = persistentSetOf()) + ExtensionReposScreen( + state = state, + onClickCreate = { }, + onOpenWebsite = { }, + onClickDelete = { }, + onClickRefresh = { }, + navigateUp = { }, + ) +} +// KMK <-- diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionApi.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionApi.kt index 659254646..5a5eb9e6e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionApi.kt @@ -16,8 +16,6 @@ import kotlinx.coroutines.awaitAll import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json import logcat.LogPriority -import mihon.domain.extensionrepo.interactor.CreateExtensionRepo.Companion.OFFICIAL_REPO_BASE_URL -import mihon.domain.extensionrepo.interactor.CreateExtensionRepo.Companion.OFFICIAL_REPO_SIGNATURE import mihon.domain.extensionrepo.interactor.GetExtensionRepo import mihon.domain.extensionrepo.interactor.UpdateExtensionRepo import mihon.domain.extensionrepo.model.ExtensionRepo @@ -48,22 +46,11 @@ internal class ExtensionApi { } suspend fun findExtensions(): List { - val officialRepo = ExtensionRepo( - baseUrl = OFFICIAL_REPO_BASE_URL, - name = "Komikku Official Extensions Repo", - shortName = "Komikku Official", - website = "https://komikku-app.github.io", - signingKeyFingerprint = OFFICIAL_REPO_SIGNATURE, - ) return withIOContext { - buildList { - // Combine built-in OFFICIAL repo's extensions list with repo's list - addAll(getExtensions(officialRepo)) - getExtensionRepo.getAll() - .map { async { getExtensions(it) } } - .awaitAll() - .flatten() - } + getExtensionRepo.getAll() + .map { async { getExtensions(it) } } + .awaitAll() + .flatten() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsScreenModel.kt index 9fa7ef20f..8fce4183b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsScreenModel.kt @@ -30,6 +30,7 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import tachiyomi.core.common.util.lang.launchIO import tachiyomi.i18n.MR +import tachiyomi.i18n.kmk.KMR import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import kotlin.time.Duration.Companion.seconds @@ -130,6 +131,11 @@ class ExtensionsScreenModel( if (languagesWithExtensions.isNotEmpty()) { itemsGroups.putAll(languagesWithExtensions) } + // KMK --> + if (_available.isEmpty()) { + itemsGroups[ExtensionUiModel.Header.Resource(KMR.strings.extensions_page_more)] = emptyList() + } + // KMK <-- itemsGroups } diff --git a/app/src/main/java/mihon/core/migration/Migration.kt b/app/src/main/java/mihon/core/migration/Migration.kt index 2c7283bb2..076b8801e 100644 --- a/app/src/main/java/mihon/core/migration/Migration.kt +++ b/app/src/main/java/mihon/core/migration/Migration.kt @@ -1,6 +1,7 @@ package mihon.core.migration interface Migration { + /** Begin version of this Migration */ val version: Float suspend operator fun invoke(migrationContext: MigrationContext): Boolean diff --git a/app/src/main/java/mihon/core/migration/migrations/Migrations.kt b/app/src/main/java/mihon/core/migration/migrations/Migrations.kt index 514825365..6cd53d0f1 100644 --- a/app/src/main/java/mihon/core/migration/migrations/Migrations.kt +++ b/app/src/main/java/mihon/core/migration/migrations/Migrations.kt @@ -45,4 +45,7 @@ val migrations: List MoveCacheToDiskSettingMigration(), MoveEncryptionSettingsToAppStateMigration(), TrustExtensionRepositoryMigration(), + // KMK --> + OfficialExtensionRepositoryMigration(), + // KMK <-- ) diff --git a/app/src/main/java/mihon/core/migration/migrations/OfficialExtensionRepositoryMigration.kt b/app/src/main/java/mihon/core/migration/migrations/OfficialExtensionRepositoryMigration.kt new file mode 100644 index 000000000..754d7efbf --- /dev/null +++ b/app/src/main/java/mihon/core/migration/migrations/OfficialExtensionRepositoryMigration.kt @@ -0,0 +1,32 @@ +package mihon.core.migration.migrations + +import logcat.LogPriority +import mihon.core.migration.Migration +import mihon.core.migration.MigrationContext +import mihon.domain.extensionrepo.exception.SaveExtensionRepoException +import mihon.domain.extensionrepo.interactor.CreateExtensionRepo +import mihon.domain.extensionrepo.repository.ExtensionRepoRepository +import tachiyomi.core.common.util.lang.withIOContext +import tachiyomi.core.common.util.system.logcat + +class OfficialExtensionRepositoryMigration : Migration { + override val version: Float = 70f + + override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext { + val extensionRepositoryRepository = + migrationContext.get() ?: return@withIOContext false + try { + extensionRepositoryRepository.upsertRepo( + baseUrl = CreateExtensionRepo.OFFICIAL_REPO_BASE_URL, + name = "Komikku Official", + shortName = "Komikku", + website = "https://komikku-app.github.io", + signingKeyFingerprint = CreateExtensionRepo.OFFICIAL_REPO_SIGNATURE, + ) + } catch (e: SaveExtensionRepoException) { + logcat(LogPriority.ERROR, e) { "Error inserting Official Extension Repo" } + return@withIOContext false + } + return@withIOContext true + } +} diff --git a/domain/src/main/java/mihon/domain/extensionrepo/interactor/CreateExtensionRepo.kt b/domain/src/main/java/mihon/domain/extensionrepo/interactor/CreateExtensionRepo.kt index 021a902aa..91b403800 100644 --- a/domain/src/main/java/mihon/domain/extensionrepo/interactor/CreateExtensionRepo.kt +++ b/domain/src/main/java/mihon/domain/extensionrepo/interactor/CreateExtensionRepo.kt @@ -15,7 +15,7 @@ class CreateExtensionRepo( suspend fun await(repoUrl: String): Result { // Do not allow invalid formats & avoid adding duplicating official repo - if (!repoUrl.matches(repoRegex) || repoUrl.startsWith(OFFICIAL_REPO_BASE_URL)) { + if (!repoUrl.matches(repoRegex)) { return Result.InvalidUrl } @@ -70,6 +70,7 @@ class CreateExtensionRepo( } companion object { + const val OFFICIAL_REPO_WEBSITE = "https://komikku-app.github.io" const val OFFICIAL_REPO_BASE_URL = "https://raw.githubusercontent.com/komikku-app/extensions/repo" // cuong-tran's key diff --git a/i18n-kmk/src/commonMain/moko-resources/base/strings.xml b/i18n-kmk/src/commonMain/moko-resources/base/strings.xml index ee25b76a4..6feeca03f 100644 --- a/i18n-kmk/src/commonMain/moko-resources/base/strings.xml +++ b/i18n-kmk/src/commonMain/moko-resources/base/strings.xml @@ -53,6 +53,7 @@ Unofficial This extension is not from the official repo. + More extensions... diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/screens/EmptyScreen.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/screens/EmptyScreen.kt index 0922f925c..b947b1064 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/screens/EmptyScreen.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/screens/EmptyScreen.kt @@ -40,11 +40,13 @@ fun EmptyScreen( stringRes: StringResource, modifier: Modifier = Modifier, actions: ImmutableList? = null, + help: @Composable (() -> Unit)? = null, ) { EmptyScreen( message = stringResource(stringRes), modifier = modifier, actions = actions, + help = help, ) } @@ -53,6 +55,7 @@ fun EmptyScreen( message: String, modifier: Modifier = Modifier, actions: ImmutableList? = null, + help: @Composable (() -> Unit)? = null, ) { val face = remember { getRandomErrorFace() } Column( @@ -80,6 +83,8 @@ fun EmptyScreen( textAlign = TextAlign.Center, ) + help?.let { help() } + if (!actions.isNullOrEmpty()) { Row( modifier = Modifier