Skip to content

Commit 5b871b8

Browse files
committed
feat: Show patches load errors in patches tab and adjust API down notification to work correctly
1 parent 9cf2bd6 commit 5b871b8

File tree

5 files changed

+95
-15
lines changed

5 files changed

+95
-15
lines changed

app/src/main/java/app/revanced/manager/domain/repository/PatchBundleRepository.kt

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ class PatchBundleRepository(
6161
private val _updateError = MutableStateFlow<Throwable?>(null)
6262
val updateError = _updateError.asStateFlow()
6363

64+
private val _apiOutageError = MutableStateFlow<Throwable?>(null)
65+
val apiOutageError = _apiOutageError.asStateFlow()
66+
6467
val sources = store.state.map { it.sources.values.toList() }
6568
val bundles = store.state.map {
6669
it.sources.mapNotNull { (uid, src) ->
@@ -353,6 +356,8 @@ class PatchBundleRepository(
353356
private val showToast: Boolean = false,
354357
private val predicate: (bundle: RemotePatchBundle) -> Boolean = { true },
355358
) : Action<State> {
359+
private var attemptedMainApiBundleUpdate = false
360+
356361
private suspend fun toast(@StringRes id: Int, vararg args: Any?) =
357362
withContext(Dispatchers.Main) { app.toast(app.getString(id, *args)) }
358363

@@ -369,6 +374,9 @@ class PatchBundleRepository(
369374
val updated = current.sources.values
370375
.filterIsInstance<RemotePatchBundle>()
371376
.filter { predicate(it) }
377+
.also { targets ->
378+
attemptedMainApiBundleUpdate = targets.any { it.uid == 0 && it is APIPatchBundle }
379+
}
372380
.map {
373381
async {
374382
Log.d(tag, "Updating patch bundle: ${it.name}")
@@ -383,6 +391,11 @@ class PatchBundleRepository(
383391
.awaitAll()
384392
.filterNotNull()
385393
.toMap()
394+
395+
if (attemptedMainApiBundleUpdate) {
396+
_apiOutageError.value = null
397+
}
398+
386399
if (updated.isEmpty()) {
387400
if (showToast) toast(R.string.patches_update_unavailable)
388401
return@coroutineScope current
@@ -404,6 +417,11 @@ class PatchBundleRepository(
404417
override suspend fun catch(exception: Exception) {
405418
Log.e(tag, "Failed to update patches", exception)
406419
_updateError.value = exception
420+
421+
if (attemptedMainApiBundleUpdate) {
422+
_apiOutageError.value = exception
423+
}
424+
407425
toast(R.string.patches_download_fail, exception.simpleMessage())
408426
}
409427
}
@@ -422,4 +440,4 @@ class PatchBundleRepository(
422440
autoUpdate = false
423441
)
424442
}
425-
}
443+
}

app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import androidx.compose.material.icons.filled.Delete
3939
import androidx.compose.material.icons.filled.KeyboardArrowDown
4040
import androidx.compose.material.icons.filled.Restore
4141
import androidx.compose.material.icons.outlined.Deselect
42+
import androidx.compose.material.icons.outlined.ErrorOutline
4243
import androidx.compose.material.icons.outlined.FilterList
4344
import androidx.compose.material.icons.outlined.Restore
4445
import androidx.compose.material.icons.outlined.Save
@@ -131,6 +132,7 @@ fun PatchesSelectorScreen(
131132
val stickyHeaderTopGap = 8.dp
132133
val readOnly = viewModel.readOnly
133134
val bundles by viewModel.bundlesFlow.collectAsStateWithLifecycle(initialValue = emptyList())
135+
val bundleLoadIssues by viewModel.bundleLoadIssuesFlow.collectAsStateWithLifecycle(initialValue = emptyMap())
134136
val pm: PM = koinInject()
135137
val pmAppList by pm.appList.collectAsStateWithLifecycle(initialValue = emptyList())
136138
val patchLazyListState = rememberLazyListState()
@@ -494,6 +496,7 @@ fun PatchesSelectorScreen(
494496
) {
495497
sections.forEach { section ->
496498
val bundle = section.bundle
499+
val loadIssueResId = bundleLoadIssues[bundle.uid]
497500

498501
stickyHeader(key = "$keyPrefix-source-${bundle.uid}") {
499502
Column(
@@ -511,7 +514,10 @@ fun PatchesSelectorScreen(
511514
onExpandToggle = { toggleBundleExpanded(bundle.uid) },
512515
onDeleteClick = { onSourceDeleteRequest?.invoke(bundle.uid) },
513516
sourceEditMode = isSourceEditMode,
514-
readOnly = readOnly
517+
readOnly = readOnly,
518+
loadIssue = loadIssueResId?.let { messageId ->
519+
stringResource(messageId)
520+
}
515521
)
516522
}
517523
}
@@ -1070,7 +1076,8 @@ private fun SourceSectionHeader(
10701076
onExpandToggle: () -> Unit,
10711077
onDeleteClick: () -> Unit,
10721078
sourceEditMode: Boolean,
1073-
readOnly: Boolean
1079+
readOnly: Boolean,
1080+
loadIssue: String?
10741081
) {
10751082
val toggleableState = when (selectionState) {
10761083
true -> ToggleableState.On
@@ -1098,12 +1105,24 @@ private fun SourceSectionHeader(
10981105
headlineContent = {
10991106
Text(text = bundle.name)
11001107
},
1101-
supportingContent = bundle.version?.takeIf { it.isNotBlank() }?.let { version ->
1102-
{
1103-
Text(
1104-
text = version,
1105-
color = MaterialTheme.colorScheme.onSurfaceVariant
1106-
)
1108+
supportingContent = {
1109+
val version = bundle.version?.takeIf { it.isNotBlank() }
1110+
if (version == null && loadIssue == null) return@ListItem
1111+
1112+
Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
1113+
version?.let {
1114+
Text(
1115+
text = it,
1116+
color = MaterialTheme.colorScheme.onSurfaceVariant
1117+
)
1118+
}
1119+
1120+
loadIssue?.let {
1121+
Text(
1122+
text = it,
1123+
color = MaterialTheme.colorScheme.error
1124+
)
1125+
}
11071126
}
11081127
},
11091128
trailingContent = {
@@ -1136,4 +1155,4 @@ private fun SourceSectionHeader(
11361155
)
11371156
HorizontalDivider()
11381157
}
1139-
}
1158+
}

app/src/main/java/app/revanced/manager/ui/viewmodel/DashboardViewModel.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class DashboardViewModel(
4646
) : ViewModel() {
4747
val availablePatches =
4848
patchBundleRepository.bundleInfoFlow.map { it.values.sumOf { bundle -> bundle.patches.size } }
49-
val bundleDownloadError = patchBundleRepository.updateError
49+
val bundleDownloadError = patchBundleRepository.apiOutageError
5050
private val contentResolver: ContentResolver = app.contentResolver
5151
private val powerManager = app.getSystemService<PowerManager>()!!
5252

app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import androidx.lifecycle.viewModelScope
1515
import androidx.lifecycle.viewmodel.compose.SavedStateHandleSaveableApi
1616
import androidx.lifecycle.viewmodel.compose.saveable
1717
import app.revanced.manager.R
18+
import app.revanced.manager.domain.bundles.PatchBundleSource
19+
import app.revanced.manager.domain.bundles.PatchBundleSource.State
1820
import app.revanced.manager.domain.manager.PreferencesManager
1921
import app.revanced.manager.domain.repository.PatchBundleRepository
2022
import app.revanced.manager.patcher.patch.PatchBundleInfo
@@ -37,6 +39,7 @@ import kotlinx.collections.immutable.toPersistentMap
3739
import kotlinx.collections.immutable.toPersistentSet
3840
import kotlinx.coroutines.CoroutineStart
3941
import kotlinx.coroutines.async
42+
import kotlinx.coroutines.flow.combine
4043
import kotlinx.coroutines.flow.first
4144
import kotlinx.coroutines.flow.flow
4245
import kotlinx.coroutines.flow.map
@@ -65,11 +68,34 @@ class PatchesSelectorViewModel(input: SelectedApplicationInfo.PatchesSelector.Vi
6568
val allowIncompatiblePatches =
6669
get<PreferencesManager>().disablePatchVersionCompatCheck.getBlocking() || appVersion == null
6770
val bundlesFlow = if (browseAllBundles) {
68-
bundleRepository.bundleInfoFlow.map { bundles ->
69-
bundles.values.map(PatchBundleInfo.Global::asReadonlyScoped)
71+
combine(bundleRepository.sources, bundleRepository.bundleInfoFlow) { sources, bundles ->
72+
mergeSourcesWithBundleInfo(
73+
sources,
74+
bundles.mapValues { (_, bundle) -> bundle.asReadonlyScoped() }
75+
)
7076
}
7177
} else {
72-
bundleRepository.scopedBundleInfoFlow(packageName, input.app.version)
78+
combine(
79+
bundleRepository.sources,
80+
bundleRepository.scopedBundleInfoFlow(packageName, input.app.version)
81+
) { sources, bundles ->
82+
mergeSourcesWithBundleInfo(
83+
sources,
84+
bundles.associateBy(PatchBundleInfo.Scoped::uid)
85+
)
86+
}
87+
}
88+
89+
val bundleLoadIssuesFlow = bundleRepository.sources.map { sources ->
90+
sources.mapNotNull { source ->
91+
val messageId = when {
92+
source.error != null -> R.string.patches_error_description
93+
source.state is State.Missing -> R.string.patches_not_downloaded
94+
else -> null
95+
} ?: return@mapNotNull null
96+
97+
source.uid to messageId
98+
}.toMap()
7399
}
74100

75101
init {
@@ -306,6 +332,13 @@ class PatchesSelectorViewModel(input: SelectedApplicationInfo.PatchesSelector.Vi
306332
private val selectionSaver: Saver<PersistentPatchSelection?, Nullable<PatchSelection>> =
307333
nullableSaver(persistentMapSaver(valueSaver = persistentSetSaver()))
308334
}
335+
336+
private fun mergeSourcesWithBundleInfo(
337+
sources: List<PatchBundleSource>,
338+
scopedBundleInfoByUid: Map<Int, PatchBundleInfo.Scoped>
339+
) = sources.map { source ->
340+
scopedBundleInfoByUid[source.uid] ?: source.emptyScopedBundleInfo()
341+
}
309342
}
310343

311344
// Versions of other types, but utilizing persistent/observable collection types.
@@ -324,3 +357,13 @@ private fun PatchBundleInfo.Global.asReadonlyScoped() = PatchBundleInfo.Scoped(
324357
incompatible = emptyList(),
325358
universal = emptyList()
326359
)
360+
361+
private fun PatchBundleSource.emptyScopedBundleInfo() = PatchBundleInfo.Scoped(
362+
name = name,
363+
version = version,
364+
uid = uid,
365+
patches = emptyList(),
366+
compatible = emptyList(),
367+
incompatible = emptyList(),
368+
universal = emptyList()
369+
)

app/src/main/res/values/strings.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ Second \"item\" text"</string>
8787
<string name="patches_missing">Missing</string>
8888
<string name="patches_error">Error</string>
8989
<string name="patches_error_description">Patches could not be loaded. Click to view the error</string>
90-
<string name="patches_not_downloaded">Patches has not been downloaded. Click here to download it</string>
90+
<string name="patches_not_downloaded">Patches has not been downloaded.</string>
9191
<string name="patches_name_default">Patches</string>
9292
<string name="patches_name_fallback">Unnamed</string>
9393

0 commit comments

Comments
 (0)