feat(app): Add ability to open available extension websites in WebView

Closes #8628
This commit is contained in:
arkon 2023-12-11 22:24:33 -05:00 committed by Claudemirovsky
parent 637f11652b
commit 6135798471
No known key found for this signature in database
GPG key ID: 82AE76162407356E
4 changed files with 183 additions and 60 deletions

View file

@ -14,6 +14,11 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Close
import androidx.compose.material.icons.outlined.GetApp
import androidx.compose.material.icons.outlined.Public
import androidx.compose.material.icons.outlined.Refresh
import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material.icons.outlined.VerifiedUser
import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Icon
@ -61,6 +66,7 @@ fun AnimeExtensionScreen(
searchQuery: String?,
onLongClickItem: (AnimeExtension) -> Unit,
onClickItemCancel: (AnimeExtension) -> Unit,
onClickItemWebView: (AnimeExtension.Available) -> Unit,
onInstallExtension: (AnimeExtension.Available) -> Unit,
onUninstallExtension: (AnimeExtension) -> Unit,
onUpdateExtension: (AnimeExtension.Installed) -> Unit,
@ -93,6 +99,7 @@ fun AnimeExtensionScreen(
contentPadding = contentPadding,
onLongClickItem = onLongClickItem,
onClickItemCancel = onClickItemCancel,
onClickItemWebView = onClickItemWebView,
onInstallExtension = onInstallExtension,
onUninstallExtension = onUninstallExtension,
onUpdateExtension = onUpdateExtension,
@ -110,6 +117,7 @@ private fun AnimeExtensionContent(
state: AnimeExtensionsScreenModel.State,
contentPadding: PaddingValues,
onLongClickItem: (AnimeExtension) -> Unit,
onClickItemWebView: (AnimeExtension.Available) -> Unit,
onClickItemCancel: (AnimeExtension) -> Unit,
onInstallExtension: (AnimeExtension.Available) -> Unit,
onUninstallExtension: (AnimeExtension) -> Unit,
@ -177,6 +185,7 @@ private fun AnimeExtensionContent(
},
onLongClickItem = onLongClickItem,
onClickItemCancel = onClickItemCancel,
onClickItemWebView = onClickItemWebView,
onClickItemAction = {
when (it) {
is AnimeExtension.Available -> onInstallExtension(it)
@ -219,6 +228,7 @@ private fun AnimeExtensionItem(
item: AnimeExtensionUiModel.Item,
onClickItem: (AnimeExtension) -> Unit,
onLongClickItem: (AnimeExtension) -> Unit,
onClickItemWebView: (AnimeExtension.Available) -> Unit,
onClickItemCancel: (AnimeExtension) -> Unit,
onClickItemAction: (AnimeExtension) -> Unit,
modifier: Modifier = Modifier,
@ -262,6 +272,7 @@ private fun AnimeExtensionItem(
AnimeExtensionItemActions(
extension = extension,
installStep = installStep,
onClickItemWebView = onClickItemWebView,
onClickItemCancel = onClickItemCancel,
onClickItemAction = onClickItemAction,
)
@ -348,43 +359,82 @@ private fun AnimeExtensionItemActions(
extension: AnimeExtension,
installStep: InstallStep,
modifier: Modifier = Modifier,
onClickItemWebView: (AnimeExtension.Available) -> Unit = {},
onClickItemCancel: (AnimeExtension) -> Unit = {},
onClickItemAction: (AnimeExtension) -> Unit = {},
) {
val isIdle = installStep.isCompleted()
Row(modifier = modifier) {
if (isIdle) {
TextButton(
onClick = { onClickItemAction(extension) },
) {
Text(
text = when (installStep) {
InstallStep.Installed -> stringResource(MR.strings.ext_installed)
InstallStep.Error -> stringResource(MR.strings.action_retry)
InstallStep.Idle -> {
when (extension) {
is AnimeExtension.Installed -> {
if (extension.hasUpdate) {
stringResource(MR.strings.ext_update)
} else {
stringResource(MR.strings.action_settings)
}
}
is AnimeExtension.Untrusted -> stringResource(MR.strings.ext_trust)
is AnimeExtension.Available -> stringResource(MR.strings.ext_install)
Row(
modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
when {
!isIdle -> {
IconButton(onClick = { onClickItemCancel(extension) }) {
Icon(
imageVector = Icons.Outlined.Close,
contentDescription = stringResource(MR.strings.action_cancel),
)
}
}
installStep == InstallStep.Error -> {
IconButton(onClick = { onClickItemAction(extension) }) {
Icon(
imageVector = Icons.Outlined.Refresh,
contentDescription = stringResource(MR.strings.action_retry),
)
}
}
installStep == InstallStep.Idle -> {
when (extension) {
is AnimeExtension.Installed -> {
if (extension.hasUpdate) {
IconButton(onClick = { onClickItemAction(extension) }) {
Icon(
imageVector = Icons.Outlined.GetApp,
contentDescription = stringResource(MR.strings.ext_update),
)
}
}
else -> error("Must not show install process text")
},
)
}
} else {
IconButton(onClick = { onClickItemCancel(extension) }) {
Icon(
imageVector = Icons.Outlined.Close,
contentDescription = stringResource(MR.strings.action_cancel),
)
IconButton(onClick = { onClickItemAction(extension) }) {
Icon(
imageVector = Icons.Outlined.Settings,
contentDescription = stringResource(MR.strings.action_settings),
)
}
}
is AnimeExtension.Untrusted -> {
IconButton(onClick = { onClickItemAction(extension) }) {
Icon(
imageVector = Icons.Outlined.VerifiedUser,
contentDescription = stringResource(MR.strings.ext_trust),
)
}
}
is AnimeExtension.Available -> {
if (extension.sources.isNotEmpty()) {
IconButton(
onClick = { onClickItemWebView(extension) },
) {
Icon(
imageVector = Icons.Outlined.Public,
contentDescription = stringResource(MR.strings.action_open_in_web_view),
)
}
}
IconButton(onClick = { onClickItemAction(extension) }) {
Icon(
imageVector = Icons.Outlined.GetApp,
contentDescription = stringResource(MR.strings.ext_install),
)
}
}
}
}
}
}
}

View file

@ -14,6 +14,11 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Close
import androidx.compose.material.icons.outlined.GetApp
import androidx.compose.material.icons.outlined.Public
import androidx.compose.material.icons.outlined.Refresh
import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material.icons.outlined.VerifiedUser
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
@ -62,6 +67,7 @@ fun MangaExtensionScreen(
searchQuery: String?,
onLongClickItem: (MangaExtension) -> Unit,
onClickItemCancel: (MangaExtension) -> Unit,
onClickItemWebView: (MangaExtension.Available) -> Unit,
onInstallExtension: (MangaExtension.Available) -> Unit,
onUninstallExtension: (MangaExtension) -> Unit,
onUpdateExtension: (MangaExtension.Installed) -> Unit,
@ -94,6 +100,7 @@ fun MangaExtensionScreen(
contentPadding = contentPadding,
onLongClickItem = onLongClickItem,
onClickItemCancel = onClickItemCancel,
onClickItemWebView = onClickItemWebView,
onInstallExtension = onInstallExtension,
onUninstallExtension = onUninstallExtension,
onUpdateExtension = onUpdateExtension,
@ -111,6 +118,7 @@ private fun ExtensionContent(
state: MangaExtensionsScreenModel.State,
contentPadding: PaddingValues,
onLongClickItem: (MangaExtension) -> Unit,
onClickItemWebView: (MangaExtension.Available) -> Unit,
onClickItemCancel: (MangaExtension) -> Unit,
onInstallExtension: (MangaExtension.Available) -> Unit,
onUninstallExtension: (MangaExtension) -> Unit,
@ -177,6 +185,7 @@ private fun ExtensionContent(
}
},
onLongClickItem = onLongClickItem,
onClickItemWebView = onClickItemWebView,
onClickItemCancel = onClickItemCancel,
onClickItemAction = {
when (it) {
@ -220,6 +229,7 @@ private fun ExtensionItem(
item: MangaExtensionUiModel.Item,
onClickItem: (MangaExtension) -> Unit,
onLongClickItem: (MangaExtension) -> Unit,
onClickItemWebView: (MangaExtension.Available) -> Unit,
onClickItemCancel: (MangaExtension) -> Unit,
onClickItemAction: (MangaExtension) -> Unit,
modifier: Modifier = Modifier,
@ -263,6 +273,7 @@ private fun ExtensionItem(
ExtensionItemActions(
extension = extension,
installStep = installStep,
onClickItemWebView = onClickItemWebView,
onClickItemCancel = onClickItemCancel,
onClickItemAction = onClickItemAction,
)
@ -349,42 +360,80 @@ private fun ExtensionItemActions(
extension: MangaExtension,
installStep: InstallStep,
modifier: Modifier = Modifier,
onClickItemWebView: (MangaExtension.Available) -> Unit = {},
onClickItemCancel: (MangaExtension) -> Unit = {},
onClickItemAction: (MangaExtension) -> Unit = {},
) {
val isIdle = installStep.isCompleted()
Row(modifier = modifier) {
if (isIdle) {
TextButton(
onClick = { onClickItemAction(extension) },
) {
Text(
text = when (installStep) {
InstallStep.Installed -> stringResource(MR.strings.ext_installed)
InstallStep.Error -> stringResource(MR.strings.action_retry)
InstallStep.Idle -> {
when (extension) {
is MangaExtension.Installed -> {
if (extension.hasUpdate) {
stringResource(MR.strings.ext_update)
} else {
stringResource(MR.strings.action_settings)
}
}
is MangaExtension.Untrusted -> stringResource(MR.strings.ext_trust)
is MangaExtension.Available -> stringResource(MR.strings.ext_install)
Row(
modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
when {
!isIdle -> {
IconButton(onClick = { onClickItemCancel(extension) }) {
Icon(
imageVector = Icons.Outlined.Close,
contentDescription = stringResource(MR.strings.action_cancel),
)
}
}
installStep == InstallStep.Error -> {
IconButton(onClick = { onClickItemAction(extension) }) {
Icon(
imageVector = Icons.Outlined.Refresh,
contentDescription = stringResource(MR.strings.action_retry),
)
}
}
installStep == InstallStep.Idle -> {
when (extension) {
is MangaExtension.Installed -> {
if (extension.hasUpdate) {
IconButton(onClick = { onClickItemAction(extension) }) {
Icon(
imageVector = Icons.Outlined.GetApp,
contentDescription = stringResource(MR.strings.ext_update),
)
}
}
else -> error("Must not show install process text")
},
)
}
} else {
IconButton(onClick = { onClickItemCancel(extension) }) {
Icon(
imageVector = Icons.Outlined.Close,
contentDescription = stringResource(MR.strings.action_cancel),
)
IconButton(onClick = { onClickItemAction(extension) }) {
Icon(
imageVector = Icons.Outlined.Settings,
contentDescription = stringResource(MR.strings.action_settings),
)
}
}
is MangaExtension.Untrusted -> {
IconButton(onClick = { onClickItemAction(extension) }) {
Icon(
imageVector = Icons.Outlined.VerifiedUser,
contentDescription = stringResource(MR.strings.ext_trust),
)
}
}
is MangaExtension.Available -> {
if (extension.sources.isNotEmpty()) {
IconButton(
onClick = { onClickItemWebView(extension) },
) {
Icon(
imageVector = Icons.Outlined.Public,
contentDescription = stringResource(MR.strings.action_open_in_web_view),
)
}
}
IconButton(onClick = { onClickItemAction(extension) }) {
Icon(
imageVector = Icons.Outlined.GetApp,
contentDescription = stringResource(MR.strings.ext_install),
)
}
}
}
}
}
}

View file

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.ui.browse.anime.extension
import eu.kanade.tachiyomi.ui.webview.WebViewScreen
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Translate
import androidx.compose.runtime.Composable
@ -53,6 +54,17 @@ fun animeExtensionsTab(
},
onClickItemCancel = extensionsScreenModel::cancelInstallUpdateExtension,
onClickUpdateAll = extensionsScreenModel::updateAllExtensions,
onClickItemWebView = { extension ->
extension.sources.getOrNull(0)?.let {
navigator.push(
WebViewScreen(
url = it.baseUrl,
initialTitle = it.name,
sourceId = it.id,
),
)
}
},
onInstallExtension = extensionsScreenModel::installExtension,
onOpenExtension = { navigator.push(AnimeExtensionDetailsScreen(it.pkgName)) },
onTrustExtension = { extensionsScreenModel.trustSignature(it.signatureHash) },

View file

@ -12,6 +12,7 @@ import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.TabContent
import eu.kanade.tachiyomi.extension.manga.model.MangaExtension
import eu.kanade.tachiyomi.ui.browse.manga.extension.details.MangaExtensionDetailsScreen
import eu.kanade.tachiyomi.ui.webview.WebViewScreen
import kotlinx.collections.immutable.persistentListOf
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.stringResource
@ -49,6 +50,17 @@ fun mangaExtensionsTab(
},
onClickItemCancel = extensionsScreenModel::cancelInstallUpdateExtension,
onClickUpdateAll = extensionsScreenModel::updateAllExtensions,
onClickItemWebView = { extension ->
extension.sources.getOrNull(0)?.let {
navigator.push(
WebViewScreen(
url = it.baseUrl,
initialTitle = it.name,
sourceId = it.id,
),
)
}
},
onInstallExtension = extensionsScreenModel::installExtension,
onOpenExtension = { navigator.push(MangaExtensionDetailsScreen(it.pkgName)) },
onTrustExtension = { extensionsScreenModel.trustSignature(it.signatureHash) },