mirror of
https://github.com/mihonapp/mihon.git
synced 2025-08-21 13:51:33 +02:00
Compare commits
7 Commits
f087135876
...
ebee275110
Author | SHA1 | Date | |
---|---|---|---|
|
ebee275110 | ||
|
015d9b3bd0 | ||
|
f2ccfb0817 | ||
|
1b60c5f0f4 | ||
|
0a91b57f67 | ||
|
bcdf17fe27 | ||
|
a08e03f5cb |
2
.github/renovate.json5
vendored
2
.github/renovate.json5
vendored
@@ -3,7 +3,7 @@
|
||||
"extends": [
|
||||
"config:base"
|
||||
],
|
||||
"schedule": ["every sunday"],
|
||||
"schedule": ["every friday"],
|
||||
"labels": ["Dependencies"],
|
||||
"packageRules": [
|
||||
{
|
||||
|
10
.github/workflows/build_pull_request.yml
vendored
10
.github/workflows/build_pull_request.yml
vendored
@@ -20,22 +20,22 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Clone repo
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
|
||||
- name: Validate Gradle Wrapper
|
||||
uses: gradle/wrapper-validation-action@v2
|
||||
uses: gradle/wrapper-validation-action@699bb18358f12c5b78b37bb0111d3a0e2276e0e2 # v2.1.1
|
||||
|
||||
- name: Dependency Review
|
||||
uses: actions/dependency-review-action@v4
|
||||
uses: actions/dependency-review-action@9129d7d40b8c12c1ed0f60400d00c92d437adcce # v4.1.3
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v4
|
||||
uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: adopt
|
||||
|
||||
- name: Set up gradle
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0
|
||||
|
||||
- name: Build app and run unit tests
|
||||
run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest
|
||||
|
12
.github/workflows/build_push.yml
vendored
12
.github/workflows/build_push.yml
vendored
@@ -17,23 +17,23 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Clone repo
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
|
||||
- name: Validate Gradle Wrapper
|
||||
uses: gradle/wrapper-validation-action@v2
|
||||
uses: gradle/wrapper-validation-action@699bb18358f12c5b78b37bb0111d3a0e2276e0e2 # v2.1.1
|
||||
|
||||
- name: Setup Android SDK
|
||||
run: |
|
||||
${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "build-tools;29.0.3"
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v4
|
||||
uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: adopt
|
||||
|
||||
- name: Set up gradle
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0
|
||||
|
||||
- name: Build app and run unit tests
|
||||
run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
|
||||
- name: Sign APK
|
||||
if: startsWith(github.ref, 'refs/tags/') && github.repository == 'mihonapp/mihon'
|
||||
uses: r0adkll/sign-android-release@v1
|
||||
uses: r0adkll/sign-android-release@349ebdef58775b1e0d8099458af0816dc79b6407 # v1
|
||||
with:
|
||||
releaseDirectory: app/build/outputs/apk/standard/release
|
||||
signingKeyBase64: ${{ secrets.SIGNING_KEY }}
|
||||
@@ -83,7 +83,7 @@ jobs:
|
||||
|
||||
- name: Create Release
|
||||
if: startsWith(github.ref, 'refs/tags/') && github.repository == 'mihonapp/mihon'
|
||||
uses: softprops/action-gh-release@v1
|
||||
uses: softprops/action-gh-release@9d7c94cfd0a1f3ed45544c887983e9fa900f0564 # v2.0.4
|
||||
with:
|
||||
tag_name: ${{ env.VERSION_TAG }}
|
||||
name: Mihon ${{ env.VERSION_TAG }}
|
||||
|
14
.github/workflows/issue_moderator.yml
vendored
14
.github/workflows/issue_moderator.yml
vendored
@@ -11,28 +11,18 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Moderate issues
|
||||
uses: tachiyomiorg/issue-moderator-action@v2.6.0
|
||||
uses: keiyoushi/issue-moderator-action@a017be83547db6e107431ce7575f53c1dfa3296a
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
duplicate-label: Duplicate
|
||||
|
||||
auto-close-rules: |
|
||||
[
|
||||
{
|
||||
"type": "body",
|
||||
"regex": ".*DELETE THIS SECTION IF YOU HAVE READ AND ACKNOWLEDGED IT.*",
|
||||
"message": "The acknowledgment section was not removed."
|
||||
},
|
||||
{
|
||||
"type": "body",
|
||||
"regex": ".*\\* (Tachiyomi version|Android version|Device): \\?.*",
|
||||
"message": "Requested information in the template was not filled out."
|
||||
},
|
||||
{
|
||||
"type": "both",
|
||||
"regex": "^(?!.*myanimelist.*).*(aniyomi|anime).*$",
|
||||
"ignoreCase": true,
|
||||
"message": "Tachiyomi does not support anime, and has no plans to support anime. In addition Tachiyomi is not affiliated with Aniyomi https://github.com/jmir1/aniyomi"
|
||||
"message": "Mihon does not support anime, and has no plans to support anime. In addition Mihon is not affiliated with Aniyomi https://github.com/jmir1/aniyomi"
|
||||
},
|
||||
{
|
||||
"type": "both",
|
||||
|
2
.github/workflows/lock.yml
vendored
2
.github/workflows/lock.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
lock:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/lock-threads@v5
|
||||
- uses: dessant/lock-threads@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771 # v5.0.1
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
issue-inactive-days: '2'
|
||||
|
13
app/src/main/java/eu/kanade/core/util/SourceUtil.kt
Normal file
13
app/src/main/java/eu/kanade/core/util/SourceUtil.kt
Normal file
@@ -0,0 +1,13 @@
|
||||
package eu.kanade.core.util
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.remember
|
||||
import tachiyomi.domain.source.service.SourceManager
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
@Composable
|
||||
fun ifSourcesLoaded(): Boolean {
|
||||
return remember { Injekt.get<SourceManager>().isInitialized }.collectAsState().value
|
||||
}
|
@@ -18,6 +18,7 @@ import kotlinx.coroutines.ensureActive
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
@@ -311,18 +312,12 @@ class DownloadCache(
|
||||
}
|
||||
|
||||
// Try to wait until extensions and sources have loaded
|
||||
var sources = getSources()
|
||||
if (sources.isEmpty()) {
|
||||
withTimeoutOrNull(30.seconds) {
|
||||
while (!extensionManager.isInitialized) {
|
||||
delay(2.seconds)
|
||||
}
|
||||
var sources = emptyList<Source>()
|
||||
withTimeoutOrNull(30.seconds) {
|
||||
extensionManager.isInitialized.first { it }
|
||||
sourceManager.isInitialized.first { it }
|
||||
|
||||
while (extensionManager.availableExtensionsFlow.value.isNotEmpty() && sources.isEmpty()) {
|
||||
delay(2.seconds)
|
||||
sources = getSources()
|
||||
}
|
||||
}
|
||||
sources = getSources()
|
||||
}
|
||||
|
||||
val sourceMap = sources.associate { provider.getSourceDirName(it).lowercase() to it.id }
|
||||
|
@@ -16,6 +16,7 @@ import eu.kanade.tachiyomi.util.system.toast
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import logcat.LogPriority
|
||||
@@ -41,8 +42,8 @@ class ExtensionManager(
|
||||
private val trustExtension: TrustExtension = Injekt.get(),
|
||||
) {
|
||||
|
||||
var isInitialized = false
|
||||
private set
|
||||
private val _isInitialized = MutableStateFlow(false)
|
||||
val isInitialized: StateFlow<Boolean> = _isInitialized.asStateFlow()
|
||||
|
||||
/**
|
||||
* API where all the available extensions can be found.
|
||||
@@ -108,7 +109,7 @@ class ExtensionManager(
|
||||
.filterIsInstance<LoadResult.Untrusted>()
|
||||
.map { it.extension }
|
||||
|
||||
isInitialized = true
|
||||
_isInitialized.value = true
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -9,6 +9,8 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -28,6 +30,9 @@ class AndroidSourceManager(
|
||||
private val sourceRepository: StubSourceRepository,
|
||||
) : SourceManager {
|
||||
|
||||
private val _isInitialized = MutableStateFlow(false)
|
||||
override val isInitialized: StateFlow<Boolean> = _isInitialized.asStateFlow()
|
||||
|
||||
private val downloadManager: DownloadManager by injectLazy()
|
||||
|
||||
private val scope = CoroutineScope(Job() + Dispatchers.IO)
|
||||
@@ -60,6 +65,7 @@ class AndroidSourceManager(
|
||||
}
|
||||
}
|
||||
sourcesMapFlow.value = mutableMap
|
||||
_isInitialized.value = true
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -31,6 +31,7 @@ import androidx.preference.forEach
|
||||
import androidx.preference.getOnBindEditTextListener
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.core.util.ifSourcesLoaded
|
||||
import eu.kanade.presentation.components.AppBar
|
||||
import eu.kanade.presentation.util.Screen
|
||||
import eu.kanade.tachiyomi.R
|
||||
@@ -40,6 +41,7 @@ import eu.kanade.tachiyomi.source.sourcePreferences
|
||||
import eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText.Companion.setIncognito
|
||||
import tachiyomi.domain.source.service.SourceManager
|
||||
import tachiyomi.presentation.core.components.material.Scaffold
|
||||
import tachiyomi.presentation.core.screens.LoadingScreen
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
@@ -47,6 +49,11 @@ class SourcePreferencesScreen(val sourceId: Long) : Screen() {
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
if (!ifSourcesLoaded()) {
|
||||
LoadingScreen()
|
||||
return
|
||||
}
|
||||
|
||||
val context = LocalContext.current
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
|
||||
|
@@ -18,6 +18,7 @@ import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.core.util.ifSourcesLoaded
|
||||
import eu.kanade.presentation.browse.BrowseSourceContent
|
||||
import eu.kanade.presentation.components.SearchToolbar
|
||||
import eu.kanade.presentation.util.Screen
|
||||
@@ -34,6 +35,7 @@ import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton
|
||||
import tachiyomi.presentation.core.components.material.Scaffold
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import tachiyomi.presentation.core.screens.LoadingScreen
|
||||
import tachiyomi.source.local.LocalSource
|
||||
|
||||
data class SourceSearchScreen(
|
||||
@@ -44,6 +46,11 @@ data class SourceSearchScreen(
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
if (!ifSourcesLoaded()) {
|
||||
LoadingScreen()
|
||||
return
|
||||
}
|
||||
|
||||
val uriHandler = LocalUriHandler.current
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
val scope = rememberCoroutineScope()
|
||||
|
@@ -35,6 +35,7 @@ import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.core.util.ifSourcesLoaded
|
||||
import eu.kanade.presentation.browse.BrowseSourceContent
|
||||
import eu.kanade.presentation.browse.MissingSourceScreen
|
||||
import eu.kanade.presentation.browse.components.BrowseSourceToolbar
|
||||
@@ -60,6 +61,7 @@ import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.components.material.Scaffold
|
||||
import tachiyomi.presentation.core.components.material.padding
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import tachiyomi.presentation.core.screens.LoadingScreen
|
||||
import tachiyomi.source.local.LocalSource
|
||||
|
||||
data class BrowseSourceScreen(
|
||||
@@ -73,6 +75,11 @@ data class BrowseSourceScreen(
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
if (!ifSourcesLoaded()) {
|
||||
LoadingScreen()
|
||||
return
|
||||
}
|
||||
|
||||
val screenModel = rememberScreenModel { BrowseSourceScreenModel(sourceId, listingQuery) }
|
||||
val state by screenModel.state.collectAsState()
|
||||
|
||||
|
@@ -10,6 +10,7 @@ import androidx.compose.runtime.setValue
|
||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.core.util.ifSourcesLoaded
|
||||
import eu.kanade.presentation.browse.GlobalSearchScreen
|
||||
import eu.kanade.presentation.util.Screen
|
||||
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreen
|
||||
@@ -23,6 +24,11 @@ class GlobalSearchScreen(
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
if (!ifSourcesLoaded()) {
|
||||
LoadingScreen()
|
||||
return
|
||||
}
|
||||
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
|
||||
val screenModel = rememberScreenModel {
|
||||
|
@@ -22,6 +22,7 @@ import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.Navigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.core.util.ifSourcesLoaded
|
||||
import eu.kanade.domain.manga.model.hasCustomCover
|
||||
import eu.kanade.domain.manga.model.toSManga
|
||||
import eu.kanade.presentation.category.components.ChangeCategoryDialog
|
||||
@@ -73,6 +74,11 @@ class MangaScreen(
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
if (!ifSourcesLoaded()) {
|
||||
LoadingScreen()
|
||||
return
|
||||
}
|
||||
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
val context = LocalContext.current
|
||||
val haptic = LocalHapticFeedback.current
|
||||
|
@@ -40,6 +40,7 @@ import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
||||
import com.google.android.material.elevation.SurfaceColors
|
||||
import com.google.android.material.transition.platform.MaterialContainerTransform
|
||||
import dev.chrisbanes.insetter.applyInsetter
|
||||
import eu.kanade.core.util.ifSourcesLoaded
|
||||
import eu.kanade.domain.base.BasePreferences
|
||||
import eu.kanade.presentation.reader.DisplayRefreshHost
|
||||
import eu.kanade.presentation.reader.OrientationSelectDialog
|
||||
@@ -344,6 +345,10 @@ class ReaderActivity : BaseActivity() {
|
||||
)
|
||||
}
|
||||
|
||||
if (!ifSourcesLoaded()) {
|
||||
return@setComposeContent
|
||||
}
|
||||
|
||||
val isHttpSource = viewModel.getSource() is HttpSource
|
||||
val isFullscreen by readerPreferences.fullscreen().collectAsState()
|
||||
val flashOnPageChange by readerPreferences.flashOnPageChange().collectAsState()
|
||||
|
@@ -47,6 +47,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
@@ -264,6 +265,7 @@ class ReaderViewModel @JvmOverloads constructor(
|
||||
try {
|
||||
val manga = getManga.await(mangaId)
|
||||
if (manga != null) {
|
||||
sourceManager.isInitialized.first { it }
|
||||
mutableState.update { it.copy(manga = manga) }
|
||||
if (chapterId == -1L) chapterId = initialChapterId
|
||||
|
||||
|
@@ -18,5 +18,7 @@ style:
|
||||
ignoreCompanionObjectPropertyDeclaration: true
|
||||
ReturnCount:
|
||||
excludeGuardClauses: true
|
||||
SerialVersionUIDInSerializableClass:
|
||||
active: false
|
||||
UnusedPrivateMember:
|
||||
ignoreAnnotated: [ 'Preview' ]
|
||||
|
@@ -4,10 +4,13 @@ import eu.kanade.tachiyomi.source.CatalogueSource
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import tachiyomi.domain.source.model.StubSource
|
||||
|
||||
interface SourceManager {
|
||||
|
||||
val isInitialized: StateFlow<Boolean>
|
||||
|
||||
val catalogueSources: Flow<List<CatalogueSource>>
|
||||
|
||||
fun get(sourceKey: Long): Source?
|
||||
|
@@ -22,7 +22,7 @@ okhttp-core = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp_ve
|
||||
okhttp-logging = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp_version" }
|
||||
okhttp-brotli = { module = "com.squareup.okhttp3:okhttp-brotli", version.ref = "okhttp_version" }
|
||||
okhttp-dnsoverhttps = { module = "com.squareup.okhttp3:okhttp-dnsoverhttps", version.ref = "okhttp_version" }
|
||||
okio = "com.squareup.okio:okio:3.8.0"
|
||||
okio = "com.squareup.okio:okio:3.9.0"
|
||||
|
||||
conscrypt-android = "org.conscrypt:conscrypt-android:2.5.2"
|
||||
|
||||
@@ -89,7 +89,7 @@ sqldelight-dialects-sql = { module = "app.cash.sqldelight:sqlite-3-38-dialect",
|
||||
sqldelight-gradle = { module = "app.cash.sqldelight:gradle-plugin", version.ref = "sqldelight" }
|
||||
|
||||
junit = "org.junit.jupiter:junit-jupiter:5.10.2"
|
||||
kotest-assertions = "io.kotest:kotest-assertions-core:5.8.0"
|
||||
kotest-assertions = "io.kotest:kotest-assertions-core:5.8.1"
|
||||
mockk = "io.mockk:mockk:1.13.10"
|
||||
|
||||
voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.ref = "voyager" }
|
||||
|
Reference in New Issue
Block a user