mirror of
https://github.com/ReVanced/revanced-manager.git
synced 2024-11-10 01:01:56 +01:00
feat: improve UX for failed or missing bundles
This commit is contained in:
parent
ec0a077539
commit
f99cdfe926
7 changed files with 114 additions and 13 deletions
|
@ -1,21 +1,30 @@
|
|||
package app.revanced.manager.ui.component.bundle
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.automirrored.outlined.ArrowRight
|
||||
import androidx.compose.material.icons.outlined.DeleteOutline
|
||||
import androidx.compose.material.icons.outlined.Share
|
||||
import androidx.compose.material.icons.outlined.Update
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
|
@ -26,7 +35,7 @@ import app.revanced.manager.domain.bundles.PatchBundleSource
|
|||
import app.revanced.manager.domain.bundles.PatchBundleSource.Extensions.asRemoteOrNull
|
||||
import app.revanced.manager.domain.bundles.PatchBundleSource.Extensions.isDefault
|
||||
import app.revanced.manager.domain.bundles.PatchBundleSource.Extensions.nameState
|
||||
import kotlinx.coroutines.flow.map
|
||||
import app.revanced.manager.ui.component.ColumnWithScrollbar
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
|
@ -35,17 +44,18 @@ fun BundleInformationDialog(
|
|||
onDismissRequest: () -> Unit,
|
||||
onDeleteRequest: () -> Unit,
|
||||
bundle: PatchBundleSource,
|
||||
onRefreshButton: () -> Unit,
|
||||
onUpdate: () -> Unit,
|
||||
) {
|
||||
val composableScope = rememberCoroutineScope()
|
||||
var viewCurrentBundlePatches by remember { mutableStateOf(false) }
|
||||
val isLocal = bundle is LocalPatchBundle
|
||||
val patchCount by remember(bundle) {
|
||||
bundle.state.map { it.patchBundleOrNull()?.patches?.size ?: 0 }
|
||||
}.collectAsStateWithLifecycle(0)
|
||||
val state by bundle.state.collectAsStateWithLifecycle()
|
||||
val props by remember(bundle) {
|
||||
bundle.propsFlow()
|
||||
}.collectAsStateWithLifecycle(null)
|
||||
val patchCount = remember(state) {
|
||||
state.patchBundleOrNull()?.patches?.size ?: 0
|
||||
}
|
||||
|
||||
if (viewCurrentBundlePatches) {
|
||||
BundlePatchesDialog(
|
||||
|
@ -70,7 +80,7 @@ fun BundleInformationDialog(
|
|||
BundleTopBar(
|
||||
title = bundleName,
|
||||
onBackClick = onDismissRequest,
|
||||
onBackIcon = {
|
||||
backIcon = {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = stringResource(R.string.back)
|
||||
|
@ -86,7 +96,7 @@ fun BundleInformationDialog(
|
|||
}
|
||||
}
|
||||
if (!isLocal) {
|
||||
IconButton(onClick = onRefreshButton) {
|
||||
IconButton(onClick = onUpdate) {
|
||||
Icon(
|
||||
Icons.Outlined.Update,
|
||||
stringResource(R.string.refresh)
|
||||
|
@ -114,7 +124,95 @@ fun BundleInformationDialog(
|
|||
onPatchesClick = {
|
||||
viewCurrentBundlePatches = true
|
||||
},
|
||||
extraFields = {
|
||||
(state as? PatchBundleSource.State.Failed)?.throwable?.let {
|
||||
var showDialog by rememberSaveable {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
if (showDialog) BundleErrorViewerDialog(
|
||||
onDismiss = { showDialog = false },
|
||||
text = remember(it) { it.stackTraceToString() }
|
||||
)
|
||||
|
||||
BundleListItem(
|
||||
headlineText = stringResource(R.string.bundle_error),
|
||||
supportingText = stringResource(R.string.bundle_error_description),
|
||||
trailingContent = {
|
||||
Icon(
|
||||
Icons.AutoMirrored.Outlined.ArrowRight,
|
||||
null
|
||||
)
|
||||
},
|
||||
modifier = Modifier.clickable { showDialog = true }
|
||||
)
|
||||
}
|
||||
|
||||
if (state is PatchBundleSource.State.Missing && !isLocal) {
|
||||
BundleListItem(
|
||||
headlineText = stringResource(R.string.bundle_error),
|
||||
supportingText = stringResource(R.string.bundle_not_downloaded),
|
||||
modifier = Modifier.clickable(onClick = onUpdate)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun BundleErrorViewerDialog(onDismiss: () -> Unit, text: String) {
|
||||
val context = LocalContext.current
|
||||
|
||||
Dialog(
|
||||
onDismissRequest = onDismiss,
|
||||
properties = DialogProperties(
|
||||
usePlatformDefaultWidth = false,
|
||||
dismissOnBackPress = true
|
||||
)
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
BundleTopBar(
|
||||
title = stringResource(R.string.bundle_error),
|
||||
onBackClick = onDismiss,
|
||||
backIcon = {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = stringResource(R.string.back)
|
||||
)
|
||||
},
|
||||
actions = {
|
||||
IconButton(
|
||||
onClick = {
|
||||
val sendIntent: Intent = Intent().apply {
|
||||
action = Intent.ACTION_SEND
|
||||
putExtra(
|
||||
Intent.EXTRA_TEXT,
|
||||
text
|
||||
)
|
||||
type = "text/plain"
|
||||
}
|
||||
|
||||
val shareIntent = Intent.createChooser(sendIntent, null)
|
||||
context.startActivity(shareIntent)
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
Icons.Outlined.Share,
|
||||
contentDescription = stringResource(R.string.share)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
ColumnWithScrollbar(
|
||||
modifier = Modifier.padding(paddingValues)
|
||||
) {
|
||||
Text(text, modifier = Modifier.horizontalScroll(rememberScrollState()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -57,7 +57,7 @@ fun BundleItem(
|
|||
onDelete()
|
||||
},
|
||||
bundle = bundle,
|
||||
onRefreshButton = onUpdate,
|
||||
onUpdate = onUpdate,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ fun BundlePatchesDialog(
|
|||
BundleTopBar(
|
||||
title = stringResource(R.string.bundle_patches),
|
||||
onBackClick = onDismissRequest,
|
||||
onBackIcon = {
|
||||
backIcon = {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = stringResource(R.string.back)
|
||||
|
|
|
@ -19,7 +19,7 @@ fun BundleTopBar(
|
|||
onBackClick: (() -> Unit)? = null,
|
||||
actions: @Composable (RowScope.() -> Unit) = {},
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null,
|
||||
onBackIcon: @Composable () -> Unit,
|
||||
backIcon: @Composable () -> Unit,
|
||||
) {
|
||||
val containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(3.0.dp)
|
||||
|
||||
|
@ -34,7 +34,7 @@ fun BundleTopBar(
|
|||
navigationIcon = {
|
||||
if (onBackClick != null) {
|
||||
IconButton(onClick = onBackClick) {
|
||||
onBackIcon()
|
||||
backIcon()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -81,7 +81,7 @@ fun ImportBundleDialog(
|
|||
BundleTopBar(
|
||||
title = stringResource(R.string.import_bundle),
|
||||
onBackClick = onDismissRequest,
|
||||
onBackIcon = {
|
||||
backIcon = {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Close,
|
||||
contentDescription = stringResource(R.string.close)
|
||||
|
|
|
@ -131,7 +131,7 @@ fun DashboardScreen(
|
|||
BundleTopBar(
|
||||
title = stringResource(R.string.bundles_selected, vm.selectedSources.size),
|
||||
onBackClick = vm::cancelSourceSelection,
|
||||
onBackIcon = {
|
||||
backIcon = {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Close,
|
||||
contentDescription = stringResource(R.string.back)
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
|
||||
<string name="bundle_missing">Missing</string>
|
||||
<string name="bundle_error">Error</string>
|
||||
<string name="bundle_error_description">Bundle could not be loaded. Click to view the error</string>
|
||||
<string name="bundle_not_downloaded">Bundle has not been downloaded. Click here to download it</string>
|
||||
<string name="bundle_name_default">Default</string>
|
||||
<string name="bundle_name_fallback">Unnamed</string>
|
||||
|
||||
|
@ -121,6 +123,7 @@
|
|||
<string name="edit">Edit</string>
|
||||
<string name="dialog_input_placeholder">Value</string>
|
||||
<string name="reset">Reset</string>
|
||||
<string name="share">Share</string>
|
||||
<string name="patch">Patch</string>
|
||||
<string name="select_from_storage">Select from storage</string>
|
||||
<string name="select_from_storage_description">Select an APK file from storage using file picker</string>
|
||||
|
|
Loading…
Reference in a new issue