mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-12-22 15:41:52 +01:00
Convert extension details to full Compose
This commit is contained in:
parent
488d8ab8cf
commit
761635b572
@ -11,6 +11,7 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
|
|||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
@ -20,9 +21,12 @@ import androidx.compose.foundation.layout.height
|
|||||||
import androidx.compose.foundation.layout.navigationBars
|
import androidx.compose.foundation.layout.navigationBars
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.statusBarsPadding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
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.History
|
||||||
import androidx.compose.material.icons.outlined.Settings
|
import androidx.compose.material.icons.outlined.Settings
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
@ -41,22 +45,25 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.platform.LocalUriHandler
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import eu.kanade.presentation.browse.components.ExtensionIcon
|
import eu.kanade.presentation.browse.components.ExtensionIcon
|
||||||
|
import eu.kanade.presentation.components.AppBar
|
||||||
|
import eu.kanade.presentation.components.AppBarActions
|
||||||
import eu.kanade.presentation.components.DIVIDER_ALPHA
|
import eu.kanade.presentation.components.DIVIDER_ALPHA
|
||||||
import eu.kanade.presentation.components.Divider
|
import eu.kanade.presentation.components.Divider
|
||||||
import eu.kanade.presentation.components.EmptyScreen
|
import eu.kanade.presentation.components.EmptyScreen
|
||||||
import eu.kanade.presentation.components.LoadingScreen
|
import eu.kanade.presentation.components.LoadingScreen
|
||||||
import eu.kanade.presentation.components.PreferenceRow
|
import eu.kanade.presentation.components.PreferenceRow
|
||||||
|
import eu.kanade.presentation.components.Scaffold
|
||||||
import eu.kanade.presentation.components.ScrollbarLazyColumn
|
import eu.kanade.presentation.components.ScrollbarLazyColumn
|
||||||
import eu.kanade.presentation.util.horizontalPadding
|
import eu.kanade.presentation.util.horizontalPadding
|
||||||
|
import eu.kanade.presentation.util.plus
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.extension.model.Extension
|
import eu.kanade.tachiyomi.extension.model.Extension
|
||||||
import eu.kanade.tachiyomi.source.ConfigurableSource
|
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||||
@ -66,11 +73,68 @@ import eu.kanade.tachiyomi.util.system.LocaleHelper
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ExtensionDetailsScreen(
|
fun ExtensionDetailsScreen(
|
||||||
nestedScrollInterop: NestedScrollConnection,
|
navigateUp: () -> Unit,
|
||||||
|
presenter: ExtensionDetailsPresenter,
|
||||||
|
onClickSourcePreferences: (sourceId: Long) -> Unit,
|
||||||
|
) {
|
||||||
|
val uriHandler = LocalUriHandler.current
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
modifier = Modifier.statusBarsPadding(),
|
||||||
|
topBar = {
|
||||||
|
AppBar(
|
||||||
|
title = stringResource(R.string.label_extension_info),
|
||||||
|
navigateUp = navigateUp,
|
||||||
|
actions = {
|
||||||
|
AppBarActions(
|
||||||
|
actions = buildList {
|
||||||
|
if (presenter.extension?.isUnofficial == false) {
|
||||||
|
add(
|
||||||
|
AppBar.Action(
|
||||||
|
title = stringResource(R.string.whats_new),
|
||||||
|
icon = Icons.Outlined.History,
|
||||||
|
onClick = { uriHandler.openUri(presenter.getChangelogUrl()) },
|
||||||
|
),
|
||||||
|
)
|
||||||
|
add(
|
||||||
|
AppBar.Action(
|
||||||
|
title = stringResource(R.string.action_faq_and_guides),
|
||||||
|
icon = Icons.Outlined.HelpOutline,
|
||||||
|
onClick = { uriHandler.openUri(presenter.getReadmeUrl()) },
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
addAll(
|
||||||
|
listOf(
|
||||||
|
AppBar.OverflowAction(
|
||||||
|
title = stringResource(R.string.action_enable_all),
|
||||||
|
onClick = { presenter.toggleSources(true) },
|
||||||
|
),
|
||||||
|
AppBar.OverflowAction(
|
||||||
|
title = stringResource(R.string.action_disable_all),
|
||||||
|
onClick = { presenter.toggleSources(false) },
|
||||||
|
),
|
||||||
|
AppBar.OverflowAction(
|
||||||
|
title = stringResource(R.string.pref_clear_cookies),
|
||||||
|
onClick = { presenter.clearCookies() },
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
) { paddingValues ->
|
||||||
|
ExtensionDetails(paddingValues, presenter, onClickSourcePreferences)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ExtensionDetails(
|
||||||
|
paddingValues: PaddingValues,
|
||||||
presenter: ExtensionDetailsPresenter,
|
presenter: ExtensionDetailsPresenter,
|
||||||
onClickUninstall: () -> Unit,
|
|
||||||
onClickSourcePreferences: (sourceId: Long) -> Unit,
|
onClickSourcePreferences: (sourceId: Long) -> Unit,
|
||||||
onClickSource: (sourceId: Long) -> Unit,
|
|
||||||
) {
|
) {
|
||||||
when {
|
when {
|
||||||
presenter.isLoading -> LoadingScreen()
|
presenter.isLoading -> LoadingScreen()
|
||||||
@ -81,8 +145,7 @@ fun ExtensionDetailsScreen(
|
|||||||
var showNsfwWarning by remember { mutableStateOf(false) }
|
var showNsfwWarning by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
ScrollbarLazyColumn(
|
ScrollbarLazyColumn(
|
||||||
modifier = Modifier.nestedScroll(nestedScrollInterop),
|
contentPadding = paddingValues + WindowInsets.navigationBars.asPaddingValues(),
|
||||||
contentPadding = WindowInsets.navigationBars.asPaddingValues(),
|
|
||||||
) {
|
) {
|
||||||
when {
|
when {
|
||||||
extension.isUnofficial ->
|
extension.isUnofficial ->
|
||||||
@ -98,7 +161,7 @@ fun ExtensionDetailsScreen(
|
|||||||
item {
|
item {
|
||||||
DetailsHeader(
|
DetailsHeader(
|
||||||
extension = extension,
|
extension = extension,
|
||||||
onClickUninstall = onClickUninstall,
|
onClickUninstall = { presenter.uninstallExtension() },
|
||||||
onClickAppInfo = {
|
onClickAppInfo = {
|
||||||
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
|
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
|
||||||
data = Uri.fromParts("package", extension.pkgName, null)
|
data = Uri.fromParts("package", extension.pkgName, null)
|
||||||
@ -119,7 +182,7 @@ fun ExtensionDetailsScreen(
|
|||||||
modifier = Modifier.animateItemPlacement(),
|
modifier = Modifier.animateItemPlacement(),
|
||||||
source = source,
|
source = source,
|
||||||
onClickSourcePreferences = onClickSourcePreferences,
|
onClickSourcePreferences = onClickSourcePreferences,
|
||||||
onClickSource = onClickSource,
|
onClickSource = { presenter.toggleSource(it) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,135 +2,35 @@ package eu.kanade.tachiyomi.ui.browse.extension.details
|
|||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Menu
|
|
||||||
import android.view.MenuInflater
|
|
||||||
import android.view.MenuItem
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import eu.kanade.presentation.browse.ExtensionDetailsScreen
|
import eu.kanade.presentation.browse.ExtensionDetailsScreen
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.ui.base.controller.FullComposeController
|
||||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.ComposeController
|
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.openInBrowser
|
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.pushController
|
import eu.kanade.tachiyomi.ui.base.controller.pushController
|
||||||
import eu.kanade.tachiyomi.util.system.logcat
|
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
|
||||||
import uy.kohesive.injekt.injectLazy
|
|
||||||
|
|
||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi")
|
||||||
class ExtensionDetailsController(bundle: Bundle? = null) :
|
class ExtensionDetailsController(
|
||||||
ComposeController<ExtensionDetailsPresenter>(bundle) {
|
bundle: Bundle? = null,
|
||||||
|
) : FullComposeController<ExtensionDetailsPresenter>(bundle) {
|
||||||
private val network: NetworkHelper by injectLazy()
|
|
||||||
|
|
||||||
constructor(pkgName: String) : this(
|
constructor(pkgName: String) : this(
|
||||||
bundleOf(PKGNAME_KEY to pkgName),
|
bundleOf(PKGNAME_KEY to pkgName),
|
||||||
)
|
)
|
||||||
|
|
||||||
init {
|
|
||||||
setHasOptionsMenu(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getTitle() = resources?.getString(R.string.label_extension_info)
|
|
||||||
|
|
||||||
override fun createPresenter() = ExtensionDetailsPresenter(args.getString(PKGNAME_KEY)!!)
|
override fun createPresenter() = ExtensionDetailsPresenter(args.getString(PKGNAME_KEY)!!)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun ComposeContent(nestedScrollInterop: NestedScrollConnection) {
|
override fun ComposeContent() {
|
||||||
ExtensionDetailsScreen(
|
ExtensionDetailsScreen(
|
||||||
nestedScrollInterop = nestedScrollInterop,
|
navigateUp = router::popCurrentController,
|
||||||
presenter = presenter,
|
presenter = presenter,
|
||||||
onClickUninstall = { presenter.uninstallExtension() },
|
|
||||||
onClickSourcePreferences = { router.pushController(SourcePreferencesController(it)) },
|
onClickSourcePreferences = { router.pushController(SourcePreferencesController(it)) },
|
||||||
onClickSource = { presenter.toggleSource(it) },
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
|
||||||
inflater.inflate(R.menu.extension_details, menu)
|
|
||||||
|
|
||||||
presenter.extension?.let { extension ->
|
|
||||||
menu.findItem(R.id.action_history).isVisible = !extension.isUnofficial
|
|
||||||
menu.findItem(R.id.action_faq_and_guides).isVisible = !extension.isUnofficial
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
|
||||||
when (item.itemId) {
|
|
||||||
R.id.action_history -> openChangelog()
|
|
||||||
R.id.action_faq_and_guides -> openReadme()
|
|
||||||
R.id.action_enable_all -> toggleAllSources(true)
|
|
||||||
R.id.action_disable_all -> toggleAllSources(false)
|
|
||||||
R.id.action_clear_cookies -> clearCookies()
|
|
||||||
}
|
|
||||||
return super.onOptionsItemSelected(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onExtensionUninstalled() {
|
fun onExtensionUninstalled() {
|
||||||
router.popCurrentController()
|
router.popCurrentController()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun toggleAllSources(enable: Boolean) {
|
|
||||||
presenter.toggleSources(enable)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun openChangelog() {
|
|
||||||
val extension = presenter.extension!!
|
|
||||||
val pkgName = extension.pkgName.substringAfter("eu.kanade.tachiyomi.extension.")
|
|
||||||
val pkgFactory = extension.pkgFactory
|
|
||||||
if (extension.hasChangelog) {
|
|
||||||
val url = createUrl(URL_EXTENSION_BLOB, pkgName, pkgFactory, "/CHANGELOG.md")
|
|
||||||
openInBrowser(url)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Falling back on GitHub commit history because there is no explicit changelog in extension
|
|
||||||
val url = createUrl(URL_EXTENSION_COMMITS, pkgName, pkgFactory)
|
|
||||||
openInBrowser(url)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun openReadme() {
|
|
||||||
val extension = presenter.extension!!
|
|
||||||
|
|
||||||
if (!extension.hasReadme) {
|
|
||||||
openInBrowser("https://tachiyomi.org/help/faq/#extensions")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val pkgName = extension.pkgName.substringAfter("eu.kanade.tachiyomi.extension.")
|
|
||||||
val pkgFactory = extension.pkgFactory
|
|
||||||
val url = createUrl(URL_EXTENSION_BLOB, pkgName, pkgFactory, "/README.md")
|
|
||||||
openInBrowser(url)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createUrl(url: String, pkgName: String, pkgFactory: String?, path: String = ""): String {
|
|
||||||
return if (!pkgFactory.isNullOrEmpty()) {
|
|
||||||
when (path.isEmpty()) {
|
|
||||||
true -> "$url/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/$pkgFactory"
|
|
||||||
else -> "$url/multisrc/overrides/$pkgFactory/" + (pkgName.split(".").lastOrNull() ?: "") + path
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
url + "/src/" + pkgName.replace(".", "/") + path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun clearCookies() {
|
|
||||||
val urls = presenter.extension?.sources
|
|
||||||
?.filterIsInstance<HttpSource>()
|
|
||||||
?.map { it.baseUrl }
|
|
||||||
?.distinct() ?: emptyList()
|
|
||||||
|
|
||||||
val cleared = urls.sumOf {
|
|
||||||
network.cookieManager.remove(it.toHttpUrl())
|
|
||||||
}
|
|
||||||
|
|
||||||
logcat { "Cleared $cleared cookies for: ${urls.joinToString()}" }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val PKGNAME_KEY = "pkg_name"
|
private const val PKGNAME_KEY = "pkg_name"
|
||||||
private const val URL_EXTENSION_COMMITS = "https://github.com/tachiyomiorg/tachiyomi-extensions/commits/master"
|
|
||||||
private const val URL_EXTENSION_BLOB = "https://github.com/tachiyomiorg/tachiyomi-extensions/blob/master"
|
|
||||||
|
@ -7,14 +7,18 @@ import eu.kanade.domain.source.interactor.ToggleSource
|
|||||||
import eu.kanade.presentation.browse.ExtensionDetailsState
|
import eu.kanade.presentation.browse.ExtensionDetailsState
|
||||||
import eu.kanade.presentation.browse.ExtensionDetailsStateImpl
|
import eu.kanade.presentation.browse.ExtensionDetailsStateImpl
|
||||||
import eu.kanade.tachiyomi.extension.ExtensionManager
|
import eu.kanade.tachiyomi.extension.ExtensionManager
|
||||||
|
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||||
import eu.kanade.tachiyomi.source.Source
|
import eu.kanade.tachiyomi.source.Source
|
||||||
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||||
|
import eu.kanade.tachiyomi.util.system.logcat
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
@ -24,6 +28,7 @@ class ExtensionDetailsPresenter(
|
|||||||
private val context: Application = Injekt.get(),
|
private val context: Application = Injekt.get(),
|
||||||
private val getExtensionSources: GetExtensionSources = Injekt.get(),
|
private val getExtensionSources: GetExtensionSources = Injekt.get(),
|
||||||
private val toggleSource: ToggleSource = Injekt.get(),
|
private val toggleSource: ToggleSource = Injekt.get(),
|
||||||
|
private val network: NetworkHelper = Injekt.get(),
|
||||||
private val extensionManager: ExtensionManager = Injekt.get(),
|
private val extensionManager: ExtensionManager = Injekt.get(),
|
||||||
) : BasePresenter<ExtensionDetailsController>(), ExtensionDetailsState by state {
|
) : BasePresenter<ExtensionDetailsController>(), ExtensionDetailsState by state {
|
||||||
|
|
||||||
@ -32,7 +37,7 @@ class ExtensionDetailsPresenter(
|
|||||||
|
|
||||||
presenterScope.launchIO {
|
presenterScope.launchIO {
|
||||||
extensionManager.getInstalledExtensionsFlow()
|
extensionManager.getInstalledExtensionsFlow()
|
||||||
.map { it.firstOrNull { it.pkgName == pkgName } }
|
.map { it.firstOrNull { pkg-> pkg.pkgName == pkgName } }
|
||||||
.collectLatest { extension ->
|
.collectLatest { extension ->
|
||||||
// If extension is null it's most likely uninstalled
|
// If extension is null it's most likely uninstalled
|
||||||
if (extension == null) {
|
if (extension == null) {
|
||||||
@ -65,6 +70,44 @@ class ExtensionDetailsPresenter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getChangelogUrl(): String {
|
||||||
|
extension ?: return ""
|
||||||
|
|
||||||
|
val pkgName = extension.pkgName.substringAfter("eu.kanade.tachiyomi.extension.")
|
||||||
|
val pkgFactory = extension.pkgFactory
|
||||||
|
if (extension.hasChangelog) {
|
||||||
|
return createUrl(URL_EXTENSION_BLOB, pkgName, pkgFactory, "/CHANGELOG.md")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Falling back on GitHub commit history because there is no explicit changelog in extension
|
||||||
|
return createUrl(URL_EXTENSION_COMMITS, pkgName, pkgFactory)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getReadmeUrl(): String {
|
||||||
|
extension ?: return ""
|
||||||
|
|
||||||
|
if (!extension.hasReadme) {
|
||||||
|
return "https://tachiyomi.org/help/faq/#extensions"
|
||||||
|
}
|
||||||
|
|
||||||
|
val pkgName = extension.pkgName.substringAfter("eu.kanade.tachiyomi.extension.")
|
||||||
|
val pkgFactory = extension.pkgFactory
|
||||||
|
return createUrl(URL_EXTENSION_BLOB, pkgName, pkgFactory, "/README.md")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clearCookies() {
|
||||||
|
val urls = extension?.sources
|
||||||
|
?.filterIsInstance<HttpSource>()
|
||||||
|
?.map { it.baseUrl }
|
||||||
|
?.distinct() ?: emptyList()
|
||||||
|
|
||||||
|
val cleared = urls.sumOf {
|
||||||
|
network.cookieManager.remove(it.toHttpUrl())
|
||||||
|
}
|
||||||
|
|
||||||
|
logcat { "Cleared $cleared cookies for: ${urls.joinToString()}" }
|
||||||
|
}
|
||||||
|
|
||||||
fun uninstallExtension() {
|
fun uninstallExtension() {
|
||||||
val extension = extension ?: return
|
val extension = extension ?: return
|
||||||
extensionManager.uninstallExtension(extension.pkgName)
|
extensionManager.uninstallExtension(extension.pkgName)
|
||||||
@ -77,6 +120,17 @@ class ExtensionDetailsPresenter(
|
|||||||
fun toggleSources(enable: Boolean) {
|
fun toggleSources(enable: Boolean) {
|
||||||
extension?.sources?.forEach { toggleSource.await(it.id, enable) }
|
extension?.sources?.forEach { toggleSource.await(it.id, enable) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun createUrl(url: String, pkgName: String, pkgFactory: String?, path: String = ""): String {
|
||||||
|
return if (!pkgFactory.isNullOrEmpty()) {
|
||||||
|
when (path.isEmpty()) {
|
||||||
|
true -> "$url/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/$pkgFactory"
|
||||||
|
else -> "$url/multisrc/overrides/$pkgFactory/" + (pkgName.split(".").lastOrNull() ?: "") + path
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
url + "/src/" + pkgName.replace(".", "/") + path
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class ExtensionSourceItem(
|
data class ExtensionSourceItem(
|
||||||
@ -84,3 +138,6 @@ data class ExtensionSourceItem(
|
|||||||
val enabled: Boolean,
|
val enabled: Boolean,
|
||||||
val labelAsName: Boolean,
|
val labelAsName: Boolean,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private const val URL_EXTENSION_COMMITS = "https://github.com/tachiyomiorg/tachiyomi-extensions/commits/master"
|
||||||
|
private const val URL_EXTENSION_BLOB = "https://github.com/tachiyomiorg/tachiyomi-extensions/blob/master"
|
||||||
|
@ -99,25 +99,14 @@ class MangaPresenter(
|
|||||||
) : BasePresenter<MangaController>() {
|
) : BasePresenter<MangaController>() {
|
||||||
|
|
||||||
private val _state: MutableStateFlow<MangaScreenState> = MutableStateFlow(MangaScreenState.Loading)
|
private val _state: MutableStateFlow<MangaScreenState> = MutableStateFlow(MangaScreenState.Loading)
|
||||||
|
|
||||||
val state = _state.asStateFlow()
|
val state = _state.asStateFlow()
|
||||||
|
|
||||||
private val successState: MangaScreenState.Success?
|
private val successState: MangaScreenState.Success?
|
||||||
get() = state.value as? MangaScreenState.Success
|
get() = state.value as? MangaScreenState.Success
|
||||||
|
|
||||||
/**
|
|
||||||
* Subscription to update the manga from the source.
|
|
||||||
*/
|
|
||||||
private var fetchMangaJob: Job? = null
|
private var fetchMangaJob: Job? = null
|
||||||
|
|
||||||
/**
|
|
||||||
* Subscription to retrieve the new list of chapters from the source.
|
|
||||||
*/
|
|
||||||
private var fetchChaptersJob: Job? = null
|
private var fetchChaptersJob: Job? = null
|
||||||
|
|
||||||
/**
|
|
||||||
* Subscription to observe download status changes.
|
|
||||||
*/
|
|
||||||
private var observeDownloadsStatusJob: Job? = null
|
private var observeDownloadsStatusJob: Job? = null
|
||||||
private var observeDownloadsPageJob: Job? = null
|
private var observeDownloadsPageJob: Job? = null
|
||||||
|
|
||||||
@ -138,7 +127,7 @@ class MangaPresenter(
|
|||||||
val isFavoritedManga: Boolean
|
val isFavoritedManga: Boolean
|
||||||
get() = manga?.favorite ?: false
|
get() = manga?.favorite ?: false
|
||||||
|
|
||||||
val processedChapters: Sequence<ChapterItem>?
|
private val processedChapters: Sequence<ChapterItem>?
|
||||||
get() = successState?.processedChapters
|
get() = successState?.processedChapters
|
||||||
|
|
||||||
private val selectedPositions: Array<Int> = arrayOf(-1, -1) // first and last selected index in list
|
private val selectedPositions: Array<Int> = arrayOf(-1, -1) // first and last selected index in list
|
||||||
@ -164,8 +153,6 @@ class MangaPresenter(
|
|||||||
override fun onCreate(savedState: Bundle?) {
|
override fun onCreate(savedState: Bundle?) {
|
||||||
super.onCreate(savedState)
|
super.onCreate(savedState)
|
||||||
|
|
||||||
// Manga info - start
|
|
||||||
|
|
||||||
presenterScope.launchIO {
|
presenterScope.launchIO {
|
||||||
val manga = getMangaAndChapters.awaitManga(mangaId)
|
val manga = getMangaAndChapters.awaitManga(mangaId)
|
||||||
|
|
||||||
@ -221,15 +208,11 @@ class MangaPresenter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
preferences.incognitoMode()
|
preferences.incognitoMode()
|
||||||
.asHotFlow { incognito ->
|
.asHotFlow { incognitoMode = it }
|
||||||
incognitoMode = incognito
|
|
||||||
}
|
|
||||||
.launchIn(presenterScope)
|
.launchIn(presenterScope)
|
||||||
|
|
||||||
preferences.downloadedOnly()
|
preferences.downloadedOnly()
|
||||||
.asHotFlow { downloadedOnly ->
|
.asHotFlow { downloadedOnlyMode = it }
|
||||||
downloadedOnlyMode = downloadedOnly
|
|
||||||
}
|
|
||||||
.launchIn(presenterScope)
|
.launchIn(presenterScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,6 +222,7 @@ class MangaPresenter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Manga info - start
|
// Manga info - start
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch manga information from source.
|
* Fetch manga information from source.
|
||||||
*/
|
*/
|
||||||
@ -395,7 +379,7 @@ class MangaPresenter(
|
|||||||
* @param manga the manga to get categories from.
|
* @param manga the manga to get categories from.
|
||||||
* @return Array of category ids the manga is in, if none returns default id
|
* @return Array of category ids the manga is in, if none returns default id
|
||||||
*/
|
*/
|
||||||
suspend fun getMangaCategoryIds(manga: DomainManga): List<Long> {
|
private suspend fun getMangaCategoryIds(manga: DomainManga): List<Long> {
|
||||||
return getCategories.await(manga.id)
|
return getCategories.await(manga.id)
|
||||||
.map { it.id }
|
.map { it.id }
|
||||||
}
|
}
|
||||||
@ -420,7 +404,7 @@ class MangaPresenter(
|
|||||||
moveMangaToCategory(categoryIds)
|
moveMangaToCategory(categoryIds)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun moveMangaToCategory(categoryIds: List<Long>) {
|
private fun moveMangaToCategory(categoryIds: List<Long>) {
|
||||||
presenterScope.launchIO {
|
presenterScope.launchIO {
|
||||||
setMangaCategories.await(mangaId, categoryIds)
|
setMangaCategories.await(mangaId, categoryIds)
|
||||||
}
|
}
|
||||||
@ -951,7 +935,7 @@ class MangaPresenter(
|
|||||||
.lastOrNull()
|
.lastOrNull()
|
||||||
?.chapterNumber?.toDouble() ?: -1.0
|
?.chapterNumber?.toDouble() ?: -1.0
|
||||||
|
|
||||||
if (latestLocalReadChapterNumber >= track.lastChapterRead) {
|
if (latestLocalReadChapterNumber > track.lastChapterRead) {
|
||||||
val updatedTrack = track.copy(
|
val updatedTrack = track.copy(
|
||||||
lastChapterRead = latestLocalReadChapterNumber,
|
lastChapterRead = latestLocalReadChapterNumber,
|
||||||
)
|
)
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/action_history"
|
|
||||||
android:icon="@drawable/ic_history_24dp"
|
|
||||||
android:title="@string/whats_new"
|
|
||||||
android:visible="false"
|
|
||||||
app:iconTint="?attr/colorOnSurface"
|
|
||||||
app:showAsAction="ifRoom" />
|
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/action_faq_and_guides"
|
|
||||||
android:icon="@drawable/ic_help_24dp"
|
|
||||||
android:title="@string/action_faq_and_guides"
|
|
||||||
android:visible="false"
|
|
||||||
app:iconTint="?attr/colorOnSurface"
|
|
||||||
app:showAsAction="ifRoom" />
|
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/action_enable_all"
|
|
||||||
android:title="@string/action_enable_all"
|
|
||||||
app:showAsAction="never" />
|
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/action_disable_all"
|
|
||||||
android:title="@string/action_disable_all"
|
|
||||||
app:showAsAction="never" />
|
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/action_clear_cookies"
|
|
||||||
android:title="@string/pref_clear_cookies"
|
|
||||||
app:showAsAction="never" />
|
|
||||||
|
|
||||||
</menu>
|
|
Loading…
Reference in New Issue
Block a user