mirror of
https://github.com/mihonapp/mihon.git
synced 2025-08-24 23:21:31 +02:00
Compare commits
8 Commits
e4f2961ad2
...
30bfeb93c4
Author | SHA1 | Date | |
---|---|---|---|
|
30bfeb93c4 | ||
|
cf93e02f47 | ||
|
52db344b21 | ||
|
2f549db3b0 | ||
|
f32244142d | ||
|
b08aabf136 | ||
|
b1de63d35e | ||
|
4ba7c6b82b |
@@ -2,6 +2,8 @@ package eu.kanade.presentation.more.settings.screen
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
@@ -12,7 +14,6 @@ import eu.kanade.presentation.more.settings.Preference
|
||||
import eu.kanade.presentation.more.settings.screen.browse.ExtensionReposScreen
|
||||
import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.authenticate
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import mihon.domain.extensionrepo.interactor.GetExtensionRepoCount
|
||||
import tachiyomi.core.common.i18n.stringResource
|
||||
import tachiyomi.i18n.MR
|
||||
@@ -35,9 +36,8 @@ object SettingsBrowseScreen : SearchableSettings {
|
||||
val sourcePreferences = remember { Injekt.get<SourcePreferences>() }
|
||||
val getExtensionRepoCount = remember { Injekt.get<GetExtensionRepoCount>() }
|
||||
|
||||
// Run this blocking. It should be a quick operation and not cause issues
|
||||
// However future enhancements may add a ViewState
|
||||
val reposCount = remember { runBlocking { getExtensionRepoCount.await() } }
|
||||
val reposFlow = remember { getExtensionRepoCount.subscribe() }
|
||||
val reposCount by reposFlow.collectAsState(0)
|
||||
|
||||
return listOf(
|
||||
Preference.PreferenceGroup(
|
||||
|
@@ -1,6 +1,5 @@
|
||||
package eu.kanade.presentation.more.settings.screen.browse
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
@@ -18,7 +17,6 @@ import eu.kanade.tachiyomi.util.system.openInBrowser
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import kotlinx.collections.immutable.toImmutableSet
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import mihon.domain.extensionrepo.model.ExtensionRepo
|
||||
import tachiyomi.presentation.core.screens.LoadingScreen
|
||||
|
||||
class ExtensionReposScreen(
|
||||
@@ -47,7 +45,7 @@ class ExtensionReposScreen(
|
||||
ExtensionReposScreen(
|
||||
state = successState,
|
||||
onClickCreate = { screenModel.showDialog(RepoDialog.Create) },
|
||||
onClickOpen = { openInBrowser(context, it) },
|
||||
onClickOpen = { context.openInBrowser(it.website) },
|
||||
onClickDelete = { screenModel.showDialog(RepoDialog.Delete(it)) },
|
||||
onClickRefresh = { screenModel.refreshRepos() },
|
||||
navigateUp = navigator::pop,
|
||||
@@ -82,16 +80,10 @@ class ExtensionReposScreen(
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
screenModel.events.collectLatest { event ->
|
||||
when (event) {
|
||||
is RepoEvent.LocalizedMessage -> {
|
||||
context.toast(event.stringRes)
|
||||
}
|
||||
if (event is RepoEvent.LocalizedMessage) {
|
||||
context.toast(event.stringRes)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun openInBrowser(context: Context, extensionRepo: ExtensionRepo) {
|
||||
context.openInBrowser(extensionRepo.website)
|
||||
}
|
||||
}
|
||||
|
@@ -34,7 +34,6 @@ class ExtensionReposScreenModel(
|
||||
|
||||
init {
|
||||
screenModelScope.launchIO {
|
||||
|
||||
getExtensionRepo.subscribeAll()
|
||||
.collectLatest { repos ->
|
||||
mutableState.update {
|
||||
@@ -56,8 +55,9 @@ class ExtensionReposScreenModel(
|
||||
when (val result = createExtensionRepo.await(name)) {
|
||||
CreateExtensionRepo.Result.InvalidUrl -> _events.send(RepoEvent.InvalidUrl)
|
||||
CreateExtensionRepo.Result.RepoAlreadyExists -> _events.send(RepoEvent.RepoAlreadyExists)
|
||||
is CreateExtensionRepo.Result.DuplicateFingerprint ->
|
||||
is CreateExtensionRepo.Result.DuplicateFingerprint -> {
|
||||
showDialog(RepoDialog.Conflict(result.oldRepo, result.newRepo))
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
@@ -1,13 +1,13 @@
|
||||
package eu.kanade.tachiyomi
|
||||
|
||||
import android.content.Context
|
||||
import android.database.sqlite.SQLiteException
|
||||
import eu.kanade.domain.source.service.SourcePreferences
|
||||
import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import logcat.LogPriority
|
||||
import mihon.domain.extensionrepo.exception.SaveExtensionRepoException
|
||||
import mihon.domain.extensionrepo.repository.ExtensionRepoRepository
|
||||
import tachiyomi.core.common.preference.Preference
|
||||
import tachiyomi.core.common.preference.PreferenceStore
|
||||
@@ -55,8 +55,8 @@ object Migrations {
|
||||
source,
|
||||
"NOFINGERPRINT-${index + 1}",
|
||||
)
|
||||
} catch (ex: SQLiteException) {
|
||||
ex.logcat(LogPriority.ERROR) { "Error Migrating Extension Repo with baseUrl: $source" }
|
||||
} catch (e: SaveExtensionRepoException) {
|
||||
logcat(LogPriority.ERROR, e) { "Error Migrating Extension Repo with baseUrl: $source" }
|
||||
}
|
||||
}
|
||||
sourcePreferences.extensionRepos().delete()
|
||||
|
@@ -1,6 +1,9 @@
|
||||
package mihon.data.repository
|
||||
|
||||
import android.database.sqlite.SQLiteException
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import mihon.domain.extensionrepo.exception.SaveExtensionRepoException
|
||||
import mihon.domain.extensionrepo.model.ExtensionRepo
|
||||
import mihon.domain.extensionrepo.repository.ExtensionRepoRepository
|
||||
import tachiyomi.data.DatabaseHandler
|
||||
@@ -24,8 +27,8 @@ class ExtensionRepoRepositoryImpl(
|
||||
return handler.awaitOneOrNull { extension_reposQueries.findOneByFingerprint(fingerprint, ::mapExtensionRepo) }
|
||||
}
|
||||
|
||||
override suspend fun getCount(): Int {
|
||||
return handler.awaitOne { extension_reposQueries.count() }.toInt()
|
||||
override fun getCount(): Flow<Int> {
|
||||
return handler.subscribeToOne { extension_reposQueries.count() }.map { it.toInt() }
|
||||
}
|
||||
|
||||
override suspend fun insertRepository(
|
||||
@@ -35,7 +38,11 @@ class ExtensionRepoRepositoryImpl(
|
||||
website: String,
|
||||
fingerprint: String,
|
||||
) {
|
||||
handler.await { extension_reposQueries.insert(baseUrl, name, shortName, website, fingerprint) }
|
||||
try {
|
||||
handler.await { extension_reposQueries.insert(baseUrl, name, shortName, website, fingerprint) }
|
||||
} catch (ex: SQLiteException) {
|
||||
throw SaveExtensionRepoException(ex)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun upsertRepository(
|
||||
@@ -45,7 +52,11 @@ class ExtensionRepoRepositoryImpl(
|
||||
website: String,
|
||||
fingerprint: String,
|
||||
) {
|
||||
handler.await { extension_reposQueries.upsert(baseUrl, name, shortName, website, fingerprint) }
|
||||
try {
|
||||
handler.await { extension_reposQueries.upsert(baseUrl, name, shortName, website, fingerprint) }
|
||||
} catch (ex: SQLiteException) {
|
||||
throw SaveExtensionRepoException(ex)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun replaceRepository(newRepo: ExtensionRepo) {
|
||||
|
@@ -54,5 +54,4 @@ WHERE fingerprint = fingerprint;
|
||||
|
||||
delete:
|
||||
DELETE FROM extension_repos
|
||||
WHERE
|
||||
base_url = :base_url;
|
||||
WHERE base_url = :base_url;
|
||||
|
@@ -5,4 +5,4 @@ CREATE TABLE extension_repos (
|
||||
short_name TEXT,
|
||||
website TEXT NOT NULL,
|
||||
fingerprint TEXT UNIQUE NOT NULL
|
||||
);
|
||||
);
|
||||
|
@@ -0,0 +1,10 @@
|
||||
package mihon.domain.extensionrepo.exception
|
||||
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* Exception to abstract over SQLiteException and SQLiteConstraintException for multiplatform.
|
||||
*
|
||||
* @param throwable the source throwable to include for tracing.
|
||||
*/
|
||||
class SaveExtensionRepoException(throwable: Throwable) : IOException("Error Saving Repository to Database", throwable)
|
@@ -1,9 +1,9 @@
|
||||
package mihon.domain.extensionrepo.interactor
|
||||
|
||||
import android.database.sqlite.SQLiteConstraintException
|
||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||
import logcat.LogPriority
|
||||
import mihon.domain.extensionrepo.api.ExtensionRepoApi
|
||||
import mihon.domain.extensionrepo.exception.SaveExtensionRepoException
|
||||
import mihon.domain.extensionrepo.model.ExtensionRepo
|
||||
import mihon.domain.extensionrepo.repository.ExtensionRepoRepository
|
||||
import okhttp3.OkHttpClient
|
||||
@@ -32,7 +32,7 @@ class CreateExtensionRepo(
|
||||
}
|
||||
|
||||
private suspend fun insert(repo: ExtensionRepo): Result {
|
||||
val result = try {
|
||||
return try {
|
||||
extensionRepoRepository.insertRepository(
|
||||
repo.baseUrl,
|
||||
repo.name,
|
||||
@@ -41,18 +41,35 @@ class CreateExtensionRepo(
|
||||
repo.fingerprint,
|
||||
)
|
||||
Result.Success
|
||||
} catch (ex: SQLiteConstraintException) {
|
||||
} catch (ex: SaveExtensionRepoException) {
|
||||
logcat(LogPriority.WARN, ex) { "SQL Conflict attempting to add new repository ${repo.baseUrl}" }
|
||||
// SQLDelight doesn't provide constraint info in exceptions.
|
||||
// First check if the conflict was on primary key. if so return RepoAlreadyExists
|
||||
// Then check if the conflict was on fingerprint. if so Return DuplicateFingerprint
|
||||
extensionRepoRepository.getRepository(baseUrl = repo.baseUrl)
|
||||
?.let { Result.RepoAlreadyExists }
|
||||
?: extensionRepoRepository.getRepositoryByFingerprint(fingerprint = repo.fingerprint)
|
||||
?.let { Result.DuplicateFingerprint(it, repo) }
|
||||
?: Result.Error
|
||||
return handleInsertionError(repo)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@Suppress("ReturnCount")
|
||||
// Three returns is much cleaner than let + elvis chaining here.
|
||||
// When matching does not work because we need to capture the result from getRepositoryByFingerprint
|
||||
/**
|
||||
* Error Handler for insert when there are trying to create new repositories
|
||||
*
|
||||
* SaveExtensionRepoException doesn't provide constraint info in exceptions.
|
||||
* First check if the conflict was on primary key. if so return RepoAlreadyExists
|
||||
* Then check if the conflict was on fingerprint. if so Return DuplicateFingerprint
|
||||
* If neither are found, there was some other Error, and return Result.Error
|
||||
*
|
||||
* @param repo Extension Repo holder for passing to DB/Error Dialog
|
||||
*/
|
||||
private suspend fun handleInsertionError(repo: ExtensionRepo): Result {
|
||||
val repoExists = extensionRepoRepository.getRepository(repo.baseUrl)
|
||||
if (repoExists != null) {
|
||||
return Result.RepoAlreadyExists
|
||||
}
|
||||
val matchingFingerprintRepo = extensionRepoRepository.getRepositoryByFingerprint(repo.fingerprint)
|
||||
if (matchingFingerprintRepo != null) {
|
||||
return Result.DuplicateFingerprint(matchingFingerprintRepo, repo)
|
||||
}
|
||||
return Result.Error
|
||||
}
|
||||
|
||||
sealed interface Result {
|
||||
|
@@ -5,5 +5,5 @@ import mihon.domain.extensionrepo.repository.ExtensionRepoRepository
|
||||
class GetExtensionRepoCount(
|
||||
private val extensionRepoRepository: ExtensionRepoRepository,
|
||||
) {
|
||||
suspend fun await() = extensionRepoRepository.getCount()
|
||||
fun subscribe() = extensionRepoRepository.getCount()
|
||||
}
|
||||
|
@@ -12,14 +12,10 @@ class UpdateExtensionRepo(
|
||||
|
||||
private val extensionRepoApi = ExtensionRepoApi(networkService.client)
|
||||
|
||||
suspend fun await(repo: ExtensionRepo): ExtensionRepo? {
|
||||
val newRepo = extensionRepoApi.fetchRepoDetails(repo.baseUrl)
|
||||
newRepo?.let {
|
||||
if (repo.fingerprint.startsWith("NOFINGERPRINT") || repo.fingerprint == newRepo.fingerprint) {
|
||||
extensionRepoRepository.upsertRepository(newRepo)
|
||||
}
|
||||
return newRepo
|
||||
suspend fun await(repo: ExtensionRepo) {
|
||||
val newRepo = extensionRepoApi.fetchRepoDetails(repo.baseUrl) ?: return
|
||||
if (repo.fingerprint.startsWith("NOFINGERPRINT") || repo.fingerprint == newRepo.fingerprint) {
|
||||
extensionRepoRepository.upsertRepository(newRepo)
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
@@ -13,7 +13,7 @@ interface ExtensionRepoRepository {
|
||||
|
||||
suspend fun getRepositoryByFingerprint(fingerprint: String): ExtensionRepo?
|
||||
|
||||
suspend fun getCount(): Int
|
||||
fun getCount(): Flow<Int>
|
||||
|
||||
suspend fun insertRepository(
|
||||
baseUrl: String,
|
||||
|
Reference in New Issue
Block a user