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
This commit is contained in:
parent
3b8752930e
commit
2536e5a77e
12 changed files with 168 additions and 41 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -157,6 +157,9 @@ private fun ExtensionContent(
|
|||
val context = LocalContext.current
|
||||
var trustState by remember { mutableStateOf<Extension.Untrusted?>(null) }
|
||||
val installGranted = rememberRequestPackageInstallsPermissionState(initialValue = true)
|
||||
// KMK -->
|
||||
val navigator = LocalNavigator.current
|
||||
// KMK <--
|
||||
|
||||
FastScrollLazyColumn(
|
||||
contentPadding = contentPadding + topSmallPaddingValues,
|
||||
|
|
@ -180,7 +183,8 @@ private fun ExtensionContent(
|
|||
when (header) {
|
||||
is ExtensionUiModel.Header.Resource -> {
|
||||
val action: @Composable RowScope.() -> Unit =
|
||||
if (header.textRes == MR.strings.ext_updates_pending) {
|
||||
when (header.textRes) {
|
||||
MR.strings.ext_updates_pending -> {
|
||||
{
|
||||
Button(onClick = { onClickUpdateAll() }) {
|
||||
Text(
|
||||
|
|
@ -191,9 +195,25 @@ private fun ExtensionContent(
|
|||
)
|
||||
}
|
||||
}
|
||||
} 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,
|
||||
modifier = Modifier
|
||||
|
|
|
|||
|
|
@ -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,6 +44,27 @@ fun ExtensionRepoCreateDialog(
|
|||
AlertDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
confirmButton = {
|
||||
// KMK -->
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween, // Distribute space between items
|
||||
verticalAlignment = Alignment.CenterVertically, // Align items vertically
|
||||
) {
|
||||
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 = {
|
||||
|
|
@ -46,10 +74,7 @@ fun ExtensionRepoCreateDialog(
|
|||
) {
|
||||
Text(text = stringResource(MR.strings.action_add))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = onDismissRequest) {
|
||||
Text(text = stringResource(MR.strings.action_cancel))
|
||||
}
|
||||
}
|
||||
},
|
||||
title = {
|
||||
|
|
@ -90,6 +115,14 @@ fun ExtensionRepoCreateDialog(
|
|||
}
|
||||
}
|
||||
|
||||
// KMK -->
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun ExtensionRepoCreateDialogPreview() {
|
||||
ExtensionRepoCreateDialog({ }, { }, persistentSetOf())
|
||||
}
|
||||
// KMK <--
|
||||
|
||||
@Composable
|
||||
fun ExtensionRepoDeleteDialog(
|
||||
onDismissRequest: () -> Unit,
|
||||
|
|
|
|||
|
|
@ -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 <--
|
||||
|
|
|
|||
|
|
@ -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,24 +46,13 @@ internal class ExtensionApi {
|
|||
}
|
||||
|
||||
suspend fun findExtensions(): List<Extension.Available> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getExtensions(extRepo: ExtensionRepo): List<Extension.Available> {
|
||||
val repoBaseUrl = extRepo.baseUrl
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -45,4 +45,7 @@ val migrations: List<Migration>
|
|||
MoveCacheToDiskSettingMigration(),
|
||||
MoveEncryptionSettingsToAppStateMigration(),
|
||||
TrustExtensionRepositoryMigration(),
|
||||
// KMK -->
|
||||
OfficialExtensionRepositoryMigration(),
|
||||
// KMK <--
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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<ExtensionRepoRepository>() ?: 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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@
|
|||
<!-- Extension section -->
|
||||
<string name="ext_unofficial">Unofficial</string>
|
||||
<string name="unofficial_extension_message">This extension is not from the official repo.</string>
|
||||
<string name="extensions_page_more">More extensions...</string>
|
||||
|
||||
<!-- Extension repos -->
|
||||
<!-- Downloads section -->
|
||||
|
|
|
|||
|
|
@ -40,11 +40,13 @@ fun EmptyScreen(
|
|||
stringRes: StringResource,
|
||||
modifier: Modifier = Modifier,
|
||||
actions: ImmutableList<EmptyScreenAction>? = 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<EmptyScreenAction>? = 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
|
||||
|
|
|
|||
Loading…
Reference in a new issue