mirror of
https://github.com/mihonapp/mihon.git
synced 2025-06-30 13:07:52 +02:00
Compare commits
13 Commits
Author | SHA1 | Date | |
---|---|---|---|
b690de55e5 | |||
83fda20078 | |||
f656a37045 | |||
c58b495433 | |||
242aeb6a68 | |||
d9969cea8a | |||
d61db5931e | |||
0ea3ac9807 | |||
f9e43f574f | |||
5ef11e61d0 | |||
48546c3db4 | |||
4d87ed496c | |||
06d12e6562 |
2
.github/ISSUE_TEMPLATE.md
vendored
2
.github/ISSUE_TEMPLATE.md
vendored
@ -3,7 +3,7 @@
|
|||||||
I acknowledge that:
|
I acknowledge that:
|
||||||
|
|
||||||
- I have updated:
|
- I have updated:
|
||||||
- To the latest version of the app (stable is v0.14.4)
|
- To the latest version of the app (stable is v0.14.5)
|
||||||
- All extensions
|
- All extensions
|
||||||
- I have tried the troubleshooting guide: https://tachiyomi.org/help/guides/troubleshooting-problems/
|
- I have tried the troubleshooting guide: https://tachiyomi.org/help/guides/troubleshooting-problems/
|
||||||
- If this is an issue with an extension, that I should be opening an issue in https://github.com/tachiyomiorg/tachiyomi-extensions
|
- If this is an issue with an extension, that I should be opening an issue in https://github.com/tachiyomiorg/tachiyomi-extensions
|
||||||
|
4
.github/ISSUE_TEMPLATE/report_issue.yml
vendored
4
.github/ISSUE_TEMPLATE/report_issue.yml
vendored
@ -53,7 +53,7 @@ body:
|
|||||||
label: Tachiyomi version
|
label: Tachiyomi version
|
||||||
description: You can find your Tachiyomi version in **More → About**.
|
description: You can find your Tachiyomi version in **More → About**.
|
||||||
placeholder: |
|
placeholder: |
|
||||||
Example: "0.14.4"
|
Example: "0.14.5"
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ body:
|
|||||||
required: true
|
required: true
|
||||||
- label: I have tried the [troubleshooting guide](https://tachiyomi.org/help/guides/troubleshooting/).
|
- label: I have tried the [troubleshooting guide](https://tachiyomi.org/help/guides/troubleshooting/).
|
||||||
required: true
|
required: true
|
||||||
- label: I have updated the app to version **[0.14.4](https://github.com/tachiyomiorg/tachiyomi/releases/latest)**.
|
- label: I have updated the app to version **[0.14.5](https://github.com/tachiyomiorg/tachiyomi/releases/latest)**.
|
||||||
required: true
|
required: true
|
||||||
- label: I have updated all installed extensions.
|
- label: I have updated all installed extensions.
|
||||||
required: true
|
required: true
|
||||||
|
2
.github/ISSUE_TEMPLATE/request_feature.yml
vendored
2
.github/ISSUE_TEMPLATE/request_feature.yml
vendored
@ -33,7 +33,7 @@ body:
|
|||||||
required: true
|
required: true
|
||||||
- label: If this is an issue with an extension, I should be opening an issue in the [extensions repository](https://github.com/tachiyomiorg/tachiyomi-extensions/issues/new/choose).
|
- label: If this is an issue with an extension, I should be opening an issue in the [extensions repository](https://github.com/tachiyomiorg/tachiyomi-extensions/issues/new/choose).
|
||||||
required: true
|
required: true
|
||||||
- label: I have updated the app to version **[0.14.4](https://github.com/tachiyomiorg/tachiyomi/releases/latest)**.
|
- label: I have updated the app to version **[0.14.5](https://github.com/tachiyomiorg/tachiyomi/releases/latest)**.
|
||||||
required: true
|
required: true
|
||||||
- label: I will fill out all of the requested information in this form.
|
- label: I will fill out all of the requested information in this form.
|
||||||
required: true
|
required: true
|
||||||
|
@ -22,8 +22,8 @@ android {
|
|||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "eu.kanade.tachiyomi"
|
applicationId = "eu.kanade.tachiyomi"
|
||||||
versionCode = 95
|
versionCode = 98
|
||||||
versionName = "0.14.4"
|
versionName = "0.14.5"
|
||||||
|
|
||||||
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
|
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
|
||||||
buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"")
|
buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"")
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package eu.kanade.presentation.browse
|
package eu.kanade.presentation.browse
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.grid.GridCells
|
import androidx.compose.foundation.lazy.grid.GridCells
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.HelpOutline
|
import androidx.compose.material.icons.outlined.HelpOutline
|
||||||
@ -11,6 +12,7 @@ import androidx.compose.material3.SnackbarHostState
|
|||||||
import androidx.compose.material3.SnackbarResult
|
import androidx.compose.material3.SnackbarResult
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.paging.LoadState
|
import androidx.paging.LoadState
|
||||||
import androidx.paging.compose.LazyPagingItems
|
import androidx.paging.compose.LazyPagingItems
|
||||||
@ -18,19 +20,22 @@ import eu.kanade.data.source.NoResultsException
|
|||||||
import eu.kanade.presentation.browse.components.BrowseSourceComfortableGrid
|
import eu.kanade.presentation.browse.components.BrowseSourceComfortableGrid
|
||||||
import eu.kanade.presentation.browse.components.BrowseSourceCompactGrid
|
import eu.kanade.presentation.browse.components.BrowseSourceCompactGrid
|
||||||
import eu.kanade.presentation.browse.components.BrowseSourceList
|
import eu.kanade.presentation.browse.components.BrowseSourceList
|
||||||
|
import eu.kanade.presentation.components.AppBar
|
||||||
import eu.kanade.presentation.components.EmptyScreen
|
import eu.kanade.presentation.components.EmptyScreen
|
||||||
import eu.kanade.presentation.components.EmptyScreenAction
|
import eu.kanade.presentation.components.EmptyScreenAction
|
||||||
import eu.kanade.presentation.components.LoadingScreen
|
import eu.kanade.presentation.components.LoadingScreen
|
||||||
|
import eu.kanade.presentation.components.Scaffold
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
|
||||||
import eu.kanade.tachiyomi.source.LocalSource
|
import eu.kanade.tachiyomi.source.LocalSource
|
||||||
|
import eu.kanade.tachiyomi.source.Source
|
||||||
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import tachiyomi.domain.library.model.LibraryDisplayMode
|
import tachiyomi.domain.library.model.LibraryDisplayMode
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BrowseSourceContent(
|
fun BrowseSourceContent(
|
||||||
source: CatalogueSource?,
|
source: Source?,
|
||||||
mangaList: LazyPagingItems<StateFlow<Manga>>,
|
mangaList: LazyPagingItems<StateFlow<Manga>>,
|
||||||
columns: GridCells,
|
columns: GridCells,
|
||||||
displayMode: LibraryDisplayMode,
|
displayMode: LibraryDisplayMode,
|
||||||
@ -139,3 +144,24 @@ fun BrowseSourceContent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun MissingSourceScreen(
|
||||||
|
source: SourceManager.StubSource,
|
||||||
|
navigateUp: () -> Unit,
|
||||||
|
) {
|
||||||
|
Scaffold(
|
||||||
|
topBar = { scrollBehavior ->
|
||||||
|
AppBar(
|
||||||
|
title = source.name,
|
||||||
|
navigateUp = navigateUp,
|
||||||
|
scrollBehavior = scrollBehavior,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
) { paddingValues ->
|
||||||
|
EmptyScreen(
|
||||||
|
message = source.getSourceNotInstalledException().message!!,
|
||||||
|
modifier = Modifier.padding(paddingValues),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -20,15 +20,15 @@ import eu.kanade.presentation.components.DropdownMenu
|
|||||||
import eu.kanade.presentation.components.RadioMenuItem
|
import eu.kanade.presentation.components.RadioMenuItem
|
||||||
import eu.kanade.presentation.components.SearchToolbar
|
import eu.kanade.presentation.components.SearchToolbar
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
|
||||||
import eu.kanade.tachiyomi.source.LocalSource
|
import eu.kanade.tachiyomi.source.LocalSource
|
||||||
|
import eu.kanade.tachiyomi.source.Source
|
||||||
import tachiyomi.domain.library.model.LibraryDisplayMode
|
import tachiyomi.domain.library.model.LibraryDisplayMode
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BrowseSourceToolbar(
|
fun BrowseSourceToolbar(
|
||||||
searchQuery: String?,
|
searchQuery: String?,
|
||||||
onSearchQueryChange: (String?) -> Unit,
|
onSearchQueryChange: (String?) -> Unit,
|
||||||
source: CatalogueSource?,
|
source: Source?,
|
||||||
displayMode: LibraryDisplayMode,
|
displayMode: LibraryDisplayMode,
|
||||||
onDisplayModeChange: (LibraryDisplayMode) -> Unit,
|
onDisplayModeChange: (LibraryDisplayMode) -> Unit,
|
||||||
navigateUp: () -> Unit,
|
navigateUp: () -> Unit,
|
||||||
|
@ -240,13 +240,16 @@ private fun ScaffoldLayout(
|
|||||||
)
|
)
|
||||||
}.fastMap { it.measure(looseConstraints) }
|
}.fastMap { it.measure(looseConstraints) }
|
||||||
|
|
||||||
val bottomBarHeight = bottomBarPlaceables.fastMaxBy { it.height }?.height
|
val bottomBarHeight = bottomBarPlaceables
|
||||||
|
.fastMaxBy { it.height }
|
||||||
|
?.height
|
||||||
|
?.takeIf { it != 0 }
|
||||||
val fabOffsetFromBottom = fabPlacement?.let {
|
val fabOffsetFromBottom = fabPlacement?.let {
|
||||||
max(bottomBarHeight ?: 0, bottomInset) + it.height + FabSpacing.roundToPx()
|
max(bottomBarHeight ?: 0, bottomInset) + it.height + FabSpacing.roundToPx()
|
||||||
}
|
}
|
||||||
|
|
||||||
val snackbarOffsetFromBottom = if (snackbarHeight != 0) {
|
val snackbarOffsetFromBottom = if (snackbarHeight != 0) {
|
||||||
snackbarHeight + (fabOffsetFromBottom ?: bottomBarHeight ?: bottomInset)
|
snackbarHeight + (fabOffsetFromBottom ?: max(bottomBarHeight ?: 0, bottomInset))
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,9 @@ fun LibraryContent(
|
|||||||
var isRefreshing by remember(pagerState.currentPage) { mutableStateOf(false) }
|
var isRefreshing by remember(pagerState.currentPage) { mutableStateOf(false) }
|
||||||
|
|
||||||
if (showPageTabs && categories.size > 1) {
|
if (showPageTabs && categories.size > 1) {
|
||||||
|
if (categories.size <= pagerState.currentPage) {
|
||||||
|
pagerState.currentPage = categories.size - 1
|
||||||
|
}
|
||||||
LibraryTabs(
|
LibraryTabs(
|
||||||
categories = categories,
|
categories = categories,
|
||||||
currentPageIndex = pagerState.currentPage,
|
currentPageIndex = pagerState.currentPage,
|
||||||
|
@ -40,6 +40,7 @@ import androidx.compose.ui.window.Dialog
|
|||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
import coil.imageLoader
|
import coil.imageLoader
|
||||||
|
import coil.request.CachePolicy
|
||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
import coil.size.Size
|
import coil.size.Size
|
||||||
import eu.kanade.presentation.components.DropdownMenu
|
import eu.kanade.presentation.components.DropdownMenu
|
||||||
@ -162,6 +163,7 @@ fun MangaCoverDialog(
|
|||||||
val request = ImageRequest.Builder(view.context)
|
val request = ImageRequest.Builder(view.context)
|
||||||
.data(coverDataProvider())
|
.data(coverDataProvider())
|
||||||
.size(Size.ORIGINAL)
|
.size(Size.ORIGINAL)
|
||||||
|
.memoryCachePolicy(CachePolicy.DISABLED)
|
||||||
.target { drawable ->
|
.target { drawable ->
|
||||||
// Copy bitmap in case it came from memory cache
|
// Copy bitmap in case it came from memory cache
|
||||||
// Because SSIV needs to thoroughly read the image
|
// Because SSIV needs to thoroughly read the image
|
||||||
|
@ -152,6 +152,6 @@ class SourceManager(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class SourceNotInstalledException(val sourceString: String) :
|
inner class SourceNotInstalledException(sourceString: String) :
|
||||||
Exception(context.getString(R.string.source_not_installed, sourceString))
|
Exception(context.getString(R.string.source_not_installed, sourceString))
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ class MigrateSearchScreenModel(
|
|||||||
.filter { it.lang in enabledLanguages }
|
.filter { it.lang in enabledLanguages }
|
||||||
.filterNot { "${it.id}" in disabledSources }
|
.filterNot { "${it.id}" in disabledSources }
|
||||||
.sortedWith(compareBy({ "${it.id}" !in pinnedSources }, { "${it.name.lowercase()} (${it.lang})" }))
|
.sortedWith(compareBy({ "${it.id}" !in pinnedSources }, { "${it.name.lowercase()} (${it.lang})" }))
|
||||||
.sortedByDescending { it.id == state.value.manga!!.id }
|
.sortedByDescending { it.id == state.value.manga!!.source }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateSearchQuery(query: String?) {
|
override fun updateSearchQuery(query: String?) {
|
||||||
|
@ -39,6 +39,7 @@ import cafe.adriel.voyager.core.screen.uniqueScreenKey
|
|||||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||||
import eu.kanade.presentation.browse.BrowseSourceContent
|
import eu.kanade.presentation.browse.BrowseSourceContent
|
||||||
|
import eu.kanade.presentation.browse.MissingSourceScreen
|
||||||
import eu.kanade.presentation.browse.components.BrowseSourceToolbar
|
import eu.kanade.presentation.browse.components.BrowseSourceToolbar
|
||||||
import eu.kanade.presentation.browse.components.RemoveMangaDialog
|
import eu.kanade.presentation.browse.components.RemoveMangaDialog
|
||||||
import eu.kanade.presentation.components.ChangeCategoryDialog
|
import eu.kanade.presentation.components.ChangeCategoryDialog
|
||||||
@ -48,7 +49,9 @@ import eu.kanade.presentation.components.Scaffold
|
|||||||
import eu.kanade.presentation.util.AssistContentScreen
|
import eu.kanade.presentation.util.AssistContentScreen
|
||||||
import eu.kanade.presentation.util.padding
|
import eu.kanade.presentation.util.padding
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
|
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||||
import eu.kanade.tachiyomi.source.LocalSource
|
import eu.kanade.tachiyomi.source.LocalSource
|
||||||
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreenModel.Listing
|
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreenModel.Listing
|
||||||
import eu.kanade.tachiyomi.ui.category.CategoryScreen
|
import eu.kanade.tachiyomi.ui.category.CategoryScreen
|
||||||
@ -73,17 +76,10 @@ data class BrowseSourceScreen(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun Content() {
|
override fun Content() {
|
||||||
val navigator = LocalNavigator.currentOrThrow
|
|
||||||
val scope = rememberCoroutineScope()
|
|
||||||
val context = LocalContext.current
|
|
||||||
val haptic = LocalHapticFeedback.current
|
|
||||||
val uriHandler = LocalUriHandler.current
|
|
||||||
|
|
||||||
val screenModel = rememberScreenModel { BrowseSourceScreenModel(sourceId, listingQuery) }
|
val screenModel = rememberScreenModel { BrowseSourceScreenModel(sourceId, listingQuery) }
|
||||||
val state by screenModel.state.collectAsState()
|
val state by screenModel.state.collectAsState()
|
||||||
|
|
||||||
val snackbarHostState = remember { SnackbarHostState() }
|
val navigator = LocalNavigator.currentOrThrow
|
||||||
|
|
||||||
val navigateUp: () -> Unit = {
|
val navigateUp: () -> Unit = {
|
||||||
when {
|
when {
|
||||||
!state.isUserQuery && state.toolbarQuery != null -> screenModel.setToolbarQuery(null)
|
!state.isUserQuery && state.toolbarQuery != null -> screenModel.setToolbarQuery(null)
|
||||||
@ -91,8 +87,21 @@ data class BrowseSourceScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val onHelpClick = { uriHandler.openUri(LocalSource.HELP_URL) }
|
if (screenModel.source is SourceManager.StubSource) {
|
||||||
|
MissingSourceScreen(
|
||||||
|
source = screenModel.source,
|
||||||
|
navigateUp = navigateUp,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val context = LocalContext.current
|
||||||
|
val haptic = LocalHapticFeedback.current
|
||||||
|
val uriHandler = LocalUriHandler.current
|
||||||
|
val snackbarHostState = remember { SnackbarHostState() }
|
||||||
|
|
||||||
|
val onHelpClick = { uriHandler.openUri(LocalSource.HELP_URL) }
|
||||||
val onWebViewClick = f@{
|
val onWebViewClick = f@{
|
||||||
val source = screenModel.source as? HttpSource ?: return@f
|
val source = screenModel.source as? HttpSource ?: return@f
|
||||||
navigator.push(
|
navigator.push(
|
||||||
@ -147,7 +156,7 @@ data class BrowseSourceScreen(
|
|||||||
Text(text = stringResource(R.string.popular))
|
Text(text = stringResource(R.string.popular))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if (screenModel.source.supportsLatest) {
|
if ((screenModel.source as CatalogueSource).supportsLatest) {
|
||||||
FilterChip(
|
FilterChip(
|
||||||
selected = state.listing == Listing.Latest,
|
selected = state.listing == Listing.Latest,
|
||||||
onClick = {
|
onClick = {
|
||||||
|
@ -102,23 +102,25 @@ class BrowseSourceScreenModel(
|
|||||||
|
|
||||||
var displayMode by sourcePreferences.sourceDisplayMode().asState(coroutineScope)
|
var displayMode by sourcePreferences.sourceDisplayMode().asState(coroutineScope)
|
||||||
|
|
||||||
val source = sourceManager.get(sourceId) as CatalogueSource
|
val source = sourceManager.getOrStub(sourceId)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
mutableState.update {
|
if (source is CatalogueSource) {
|
||||||
var query: String? = null
|
mutableState.update {
|
||||||
var listing = it.listing
|
var query: String? = null
|
||||||
|
var listing = it.listing
|
||||||
|
|
||||||
if (listing is Listing.Search) {
|
if (listing is Listing.Search) {
|
||||||
query = listing.query
|
query = listing.query
|
||||||
listing = Listing.Search(query, source.getFilterList())
|
listing = Listing.Search(query, source.getFilterList())
|
||||||
|
}
|
||||||
|
|
||||||
|
it.copy(
|
||||||
|
listing = listing,
|
||||||
|
filters = source.getFilterList(),
|
||||||
|
toolbarQuery = query,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
it.copy(
|
|
||||||
listing = listing,
|
|
||||||
filters = source.getFilterList(),
|
|
||||||
toolbarQuery = query,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,6 +164,8 @@ class BrowseSourceScreenModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun resetFilters() {
|
fun resetFilters() {
|
||||||
|
if (source !is CatalogueSource) return
|
||||||
|
|
||||||
mutableState.update { it.copy(filters = source.getFilterList()) }
|
mutableState.update { it.copy(filters = source.getFilterList()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,6 +174,8 @@ class BrowseSourceScreenModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun search(query: String? = null, filters: FilterList? = null) {
|
fun search(query: String? = null, filters: FilterList? = null) {
|
||||||
|
if (source !is CatalogueSource) return
|
||||||
|
|
||||||
val input = state.value.listing as? Listing.Search
|
val input = state.value.listing as? Listing.Search
|
||||||
?: Listing.Search(query = null, filters = source.getFilterList())
|
?: Listing.Search(query = null, filters = source.getFilterList())
|
||||||
|
|
||||||
@ -185,6 +191,8 @@ class BrowseSourceScreenModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun searchGenre(genreName: String) {
|
fun searchGenre(genreName: String) {
|
||||||
|
if (source !is CatalogueSource) return
|
||||||
|
|
||||||
val defaultFilters = source.getFilterList()
|
val defaultFilters = source.getFilterList()
|
||||||
var genreExists = false
|
var genreExists = false
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ import androidx.compose.animation.fadeOut
|
|||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.PlayArrow
|
import androidx.compose.material.icons.filled.PlayArrow
|
||||||
@ -226,7 +225,6 @@ object DownloadQueueScreen : Screen {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
expanded = fabExpanded,
|
expanded = fabExpanded,
|
||||||
modifier = Modifier.navigationBarsPadding(),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -3,12 +3,14 @@ package eu.kanade.tachiyomi.ui.main
|
|||||||
import android.animation.ValueAnimator
|
import android.animation.ValueAnimator
|
||||||
import android.app.SearchManager
|
import android.app.SearchManager
|
||||||
import android.app.assist.AssistContent
|
import android.app.assist.AssistContent
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
@ -41,6 +43,7 @@ import androidx.core.animation.doOnEnd
|
|||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import androidx.core.splashscreen.SplashScreen
|
import androidx.core.splashscreen.SplashScreen
|
||||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||||
|
import androidx.core.util.Consumer
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
|
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
|
||||||
import androidx.interpolator.view.animation.LinearOutSlowInInterpolator
|
import androidx.interpolator.view.animation.LinearOutSlowInInterpolator
|
||||||
@ -85,7 +88,10 @@ import eu.kanade.tachiyomi.util.system.openInBrowser
|
|||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import eu.kanade.tachiyomi.util.view.setComposeContent
|
import eu.kanade.tachiyomi.util.view.setComposeContent
|
||||||
import kotlinx.coroutines.cancel
|
import kotlinx.coroutines.cancel
|
||||||
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.flow.callbackFlow
|
||||||
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.flow.drop
|
import kotlinx.coroutines.flow.drop
|
||||||
import kotlinx.coroutines.flow.filter
|
import kotlinx.coroutines.flow.filter
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
@ -119,8 +125,7 @@ class MainActivity : BaseActivity() {
|
|||||||
*/
|
*/
|
||||||
private var settingsSheet: LibrarySettingsSheet? = null
|
private var settingsSheet: LibrarySettingsSheet? = null
|
||||||
|
|
||||||
private var isHandlingShortcut: Boolean = false
|
private var navigator: Navigator? = null
|
||||||
private lateinit var navigator: Navigator
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
// Prevent splash screen showing up on configuration changes
|
// Prevent splash screen showing up on configuration changes
|
||||||
@ -210,7 +215,7 @@ class MainActivity : BaseActivity() {
|
|||||||
|
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
// Set start screen
|
// Set start screen
|
||||||
handleIntentAction(intent)
|
handleIntentAction(intent, navigator)
|
||||||
|
|
||||||
// Reset Incognito Mode on relaunch
|
// Reset Incognito Mode on relaunch
|
||||||
preferences.incognitoMode().set(false)
|
preferences.incognitoMode().set(false)
|
||||||
@ -257,6 +262,7 @@ class MainActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CheckForUpdate()
|
CheckForUpdate()
|
||||||
|
HandleOnNewIntent(context = context, navigator = navigator)
|
||||||
}
|
}
|
||||||
|
|
||||||
var showChangelog by remember { mutableStateOf(didMigration && !BuildConfig.DEBUG) }
|
var showChangelog by remember { mutableStateOf(didMigration && !BuildConfig.DEBUG) }
|
||||||
@ -288,7 +294,7 @@ class MainActivity : BaseActivity() {
|
|||||||
|
|
||||||
override fun onProvideAssistContent(outContent: AssistContent) {
|
override fun onProvideAssistContent(outContent: AssistContent) {
|
||||||
super.onProvideAssistContent(outContent)
|
super.onProvideAssistContent(outContent)
|
||||||
when (val screen = navigator.lastItem) {
|
when (val screen = navigator?.lastItem) {
|
||||||
is AssistContentScreen -> {
|
is AssistContentScreen -> {
|
||||||
screen.onProvideAssistUrl()?.let { outContent.webUri = it.toUri() }
|
screen.onProvideAssistUrl()?.let { outContent.webUri = it.toUri() }
|
||||||
}
|
}
|
||||||
@ -319,6 +325,18 @@ class MainActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun HandleOnNewIntent(context: Context, navigator: Navigator) {
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
callbackFlow<Intent> {
|
||||||
|
val componentActivity = context as ComponentActivity
|
||||||
|
val consumer = Consumer<Intent> { trySend(it) }
|
||||||
|
componentActivity.addOnNewIntentListener(consumer)
|
||||||
|
awaitClose { componentActivity.removeOnNewIntentListener(consumer) }
|
||||||
|
}.collectLatest { handleIntentAction(it, navigator) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun CheckForUpdate() {
|
private fun CheckForUpdate() {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
@ -387,37 +405,26 @@ class MainActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNewIntent(intent: Intent) {
|
private fun handleIntentAction(intent: Intent, navigator: Navigator): Boolean {
|
||||||
lifecycleScope.launch {
|
|
||||||
val handle = handleIntentAction(intent)
|
|
||||||
if (!handle) {
|
|
||||||
super.onNewIntent(intent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun handleIntentAction(intent: Intent): Boolean {
|
|
||||||
val notificationId = intent.getIntExtra("notificationId", -1)
|
val notificationId = intent.getIntExtra("notificationId", -1)
|
||||||
if (notificationId > -1) {
|
if (notificationId > -1) {
|
||||||
NotificationReceiver.dismissNotification(applicationContext, notificationId, intent.getIntExtra("groupId", 0))
|
NotificationReceiver.dismissNotification(applicationContext, notificationId, intent.getIntExtra("groupId", 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
isHandlingShortcut = true
|
val tabToOpen = when (intent.action) {
|
||||||
|
Constants.SHORTCUT_LIBRARY -> HomeScreen.Tab.Library()
|
||||||
when (intent.action) {
|
|
||||||
Constants.SHORTCUT_LIBRARY -> HomeScreen.openTab(HomeScreen.Tab.Library())
|
|
||||||
Constants.SHORTCUT_MANGA -> {
|
Constants.SHORTCUT_MANGA -> {
|
||||||
val idToOpen = intent.extras?.getLong(Constants.MANGA_EXTRA) ?: return false
|
val idToOpen = intent.extras?.getLong(Constants.MANGA_EXTRA) ?: return false
|
||||||
navigator.popUntilRoot()
|
navigator.popUntilRoot()
|
||||||
HomeScreen.openTab(HomeScreen.Tab.Library(idToOpen))
|
HomeScreen.Tab.Library(idToOpen)
|
||||||
}
|
}
|
||||||
Constants.SHORTCUT_UPDATES -> HomeScreen.openTab(HomeScreen.Tab.Updates)
|
Constants.SHORTCUT_UPDATES -> HomeScreen.Tab.Updates
|
||||||
Constants.SHORTCUT_HISTORY -> HomeScreen.openTab(HomeScreen.Tab.History)
|
Constants.SHORTCUT_HISTORY -> HomeScreen.Tab.History
|
||||||
Constants.SHORTCUT_SOURCES -> HomeScreen.openTab(HomeScreen.Tab.Browse(false))
|
Constants.SHORTCUT_SOURCES -> HomeScreen.Tab.Browse(false)
|
||||||
Constants.SHORTCUT_EXTENSIONS -> HomeScreen.openTab(HomeScreen.Tab.Browse(true))
|
Constants.SHORTCUT_EXTENSIONS -> HomeScreen.Tab.Browse(true)
|
||||||
Constants.SHORTCUT_DOWNLOADS -> {
|
Constants.SHORTCUT_DOWNLOADS -> {
|
||||||
navigator.popUntilRoot()
|
navigator.popUntilRoot()
|
||||||
HomeScreen.openTab(HomeScreen.Tab.More(toDownloads = true))
|
HomeScreen.Tab.More(toDownloads = true)
|
||||||
}
|
}
|
||||||
Intent.ACTION_SEARCH, Intent.ACTION_SEND, "com.google.android.gms.actions.SEARCH_ACTION" -> {
|
Intent.ACTION_SEARCH, Intent.ACTION_SEND, "com.google.android.gms.actions.SEARCH_ACTION" -> {
|
||||||
// If the intent match the "standard" Android search intent
|
// If the intent match the "standard" Android search intent
|
||||||
@ -429,6 +436,7 @@ class MainActivity : BaseActivity() {
|
|||||||
navigator.popUntilRoot()
|
navigator.popUntilRoot()
|
||||||
navigator.push(GlobalSearchScreen(query))
|
navigator.push(GlobalSearchScreen(query))
|
||||||
}
|
}
|
||||||
|
null
|
||||||
}
|
}
|
||||||
INTENT_SEARCH -> {
|
INTENT_SEARCH -> {
|
||||||
val query = intent.getStringExtra(INTENT_SEARCH_QUERY)
|
val query = intent.getStringExtra(INTENT_SEARCH_QUERY)
|
||||||
@ -437,15 +445,16 @@ class MainActivity : BaseActivity() {
|
|||||||
navigator.popUntilRoot()
|
navigator.popUntilRoot()
|
||||||
navigator.push(GlobalSearchScreen(query, filter))
|
navigator.push(GlobalSearchScreen(query, filter))
|
||||||
}
|
}
|
||||||
|
null
|
||||||
}
|
}
|
||||||
else -> {
|
else -> return false
|
||||||
isHandlingShortcut = false
|
}
|
||||||
return false
|
|
||||||
}
|
if (tabToOpen != null) {
|
||||||
|
lifecycleScope.launch { HomeScreen.openTab(tabToOpen) }
|
||||||
}
|
}
|
||||||
|
|
||||||
ready = true
|
ready = true
|
||||||
isHandlingShortcut = false
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -456,7 +465,7 @@ class MainActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onBackPressed() {
|
override fun onBackPressed() {
|
||||||
if (navigator.size == 1 &&
|
if (navigator?.size == 1 &&
|
||||||
!onBackPressedDispatcher.hasEnabledCallbacks() &&
|
!onBackPressedDispatcher.hasEnabledCallbacks() &&
|
||||||
libraryPreferences.autoClearChapterCache().get()
|
libraryPreferences.autoClearChapterCache().get()
|
||||||
) {
|
) {
|
||||||
|
@ -871,7 +871,7 @@ class ReaderActivity : BaseActivity() {
|
|||||||
* the viewer is reaching the beginning or end of a chapter or the transition page is active.
|
* the viewer is reaching the beginning or end of a chapter or the transition page is active.
|
||||||
*/
|
*/
|
||||||
fun requestPreloadChapter(chapter: ReaderChapter) {
|
fun requestPreloadChapter(chapter: ReaderChapter) {
|
||||||
lifecycleScope.launch { viewModel.preloadChapter(chapter) }
|
lifecycleScope.launchIO { viewModel.preloadChapter(chapter) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -177,10 +177,11 @@ class ReaderViewModel(
|
|||||||
}.run {
|
}.run {
|
||||||
if (readerPreferences.skipDupe().get()) {
|
if (readerPreferences.skipDupe().get()) {
|
||||||
groupBy { it.chapterNumber }
|
groupBy { it.chapterNumber }
|
||||||
.mapValues { (_, chapters) ->
|
.map { (_, chapters) ->
|
||||||
chapters.find { it.id == chapterId || it.scanlator == selectedChapter.scanlator } ?: chapters.first()
|
chapters.find { it.id == selectedChapter.id }
|
||||||
|
?: chapters.find { it.scanlator == selectedChapter.scanlator }
|
||||||
|
?: chapters.first()
|
||||||
}
|
}
|
||||||
.values
|
|
||||||
} else {
|
} else {
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
@ -364,6 +365,10 @@ class ReaderViewModel(
|
|||||||
* that the user doesn't have to wait too long to continue reading.
|
* that the user doesn't have to wait too long to continue reading.
|
||||||
*/
|
*/
|
||||||
private suspend fun preload(chapter: ReaderChapter) {
|
private suspend fun preload(chapter: ReaderChapter) {
|
||||||
|
if (chapter.state is ReaderChapter.State.Loaded || chapter.state == ReaderChapter.State.Loading) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (chapter.pageLoader is HttpPageLoader) {
|
if (chapter.pageLoader is HttpPageLoader) {
|
||||||
val manga = manga ?: return
|
val manga = manga ?: return
|
||||||
val dbChapter = chapter.chapter
|
val dbChapter = chapter.chapter
|
||||||
@ -383,20 +388,17 @@ class ReaderViewModel(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
logcat { "Preloading ${chapter.chapter.url}" }
|
|
||||||
|
|
||||||
val loader = loader ?: return
|
val loader = loader ?: return
|
||||||
withIOContext {
|
try {
|
||||||
try {
|
logcat { "Preloading ${chapter.chapter.url}" }
|
||||||
loader.loadChapter(chapter)
|
loader.loadChapter(chapter)
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
if (e is CancellationException) {
|
if (e is CancellationException) {
|
||||||
throw e
|
throw e
|
||||||
}
|
|
||||||
return@withIOContext
|
|
||||||
}
|
}
|
||||||
eventChannel.trySend(Event.ReloadViewerChapters)
|
return
|
||||||
}
|
}
|
||||||
|
eventChannel.trySend(Event.ReloadViewerChapters)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -580,7 +582,12 @@ class ReaderViewModel(
|
|||||||
val sChapter = getCurrentChapter()?.chapter ?: return null
|
val sChapter = getCurrentChapter()?.chapter ?: return null
|
||||||
val source = getSource() ?: return null
|
val source = getSource() ?: return null
|
||||||
|
|
||||||
return source.getChapterUrl(sChapter)
|
return try {
|
||||||
|
source.getChapterUrl(sChapter)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logcat(LogPriority.ERROR, e)
|
||||||
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -17,6 +17,7 @@ import eu.kanade.tachiyomi.util.system.openInBrowser
|
|||||||
import eu.kanade.tachiyomi.util.system.toShareIntent
|
import eu.kanade.tachiyomi.util.system.toShareIntent
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import eu.kanade.tachiyomi.util.view.setComposeContent
|
import eu.kanade.tachiyomi.util.view.setComposeContent
|
||||||
|
import logcat.LogPriority
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
import tachiyomi.core.util.system.logcat
|
import tachiyomi.core.util.system.logcat
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
@ -47,7 +48,11 @@ class WebViewActivity : BaseActivity() {
|
|||||||
|
|
||||||
var headers = emptyMap<String, String>()
|
var headers = emptyMap<String, String>()
|
||||||
(sourceManager.get(intent.extras!!.getLong(SOURCE_KEY)) as? HttpSource)?.let { source ->
|
(sourceManager.get(intent.extras!!.getLong(SOURCE_KEY)) as? HttpSource)?.let { source ->
|
||||||
headers = source.headers.toMultimap().mapValues { it.value.getOrNull(0) ?: "" }
|
try {
|
||||||
|
headers = source.headers.toMultimap().mapValues { it.value.getOrNull(0) ?: "" }
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logcat(LogPriority.ERROR, e) { "Failed to build headers" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setComposeContent {
|
setComposeContent {
|
||||||
|
@ -10,6 +10,7 @@ import eu.kanade.tachiyomi.source.online.HttpSource
|
|||||||
import eu.kanade.tachiyomi.util.system.openInBrowser
|
import eu.kanade.tachiyomi.util.system.openInBrowser
|
||||||
import eu.kanade.tachiyomi.util.system.toShareIntent
|
import eu.kanade.tachiyomi.util.system.toShareIntent
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
|
import logcat.LogPriority
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
import tachiyomi.core.util.system.logcat
|
import tachiyomi.core.util.system.logcat
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
@ -25,7 +26,11 @@ class WebViewScreenModel(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
sourceId?.let { sourceManager.get(it) as? HttpSource }?.let { source ->
|
sourceId?.let { sourceManager.get(it) as? HttpSource }?.let { source ->
|
||||||
headers = source.headers.toMultimap().mapValues { it.value.getOrNull(0) ?: "" }
|
try {
|
||||||
|
headers = source.headers.toMultimap().mapValues { it.value.getOrNull(0) ?: "" }
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logcat(LogPriority.ERROR, e) { "Failed to build headers" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user