fix: Save FAB freaking out in select patches screen

This commit is contained in:
Ax333l
2026-01-09 21:33:08 +01:00
parent fe84b22b6f
commit 4c0b6b02e9
2 changed files with 54 additions and 35 deletions

View File

@@ -49,10 +49,12 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.alpha
@@ -81,9 +83,11 @@ import app.revanced.manager.util.Options
import app.revanced.manager.util.PatchSelection import app.revanced.manager.util.PatchSelection
import app.revanced.manager.util.isScrollingUp import app.revanced.manager.util.isScrollingUp
import app.revanced.manager.util.transparentListItemColors import app.revanced.manager.util.transparentListItemColors
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.sample
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class) @OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class, FlowPreview::class)
@Composable @Composable
fun PatchesSelectorScreen( fun PatchesSelectorScreen(
onSave: (PatchSelection?, Options) -> Unit, onSave: (PatchSelection?, Options) -> Unit,
@@ -231,7 +235,8 @@ fun PatchesSelectorScreen(
viewModel.selectionWarningEnabled -> showSelectionWarning = true viewModel.selectionWarningEnabled -> showSelectionWarning = true
// Show universal warning if universal patch is selected and the toggle is off // Show universal warning if universal patch is selected and the toggle is off
patch.compatiblePackages == null && viewModel.universalPatchWarningEnabled -> showUniversalWarning = true patch.compatiblePackages == null && viewModel.universalPatchWarningEnabled -> showUniversalWarning =
true
// Toggle the patch otherwise // Toggle the patch otherwise
else -> viewModel.togglePatch(uid, patch) else -> viewModel.togglePatch(uid, patch)
@@ -360,6 +365,21 @@ fun PatchesSelectorScreen(
) { ) {
Icon(Icons.Outlined.Restore, stringResource(R.string.reset)) Icon(Icons.Outlined.Restore, stringResource(R.string.reset))
} }
val isScrollingUp =
patchLazyListStates.getOrNull(pagerState.currentPage)?.isScrollingUp()
val expanded by produceState(true, isScrollingUp) {
val state = isScrollingUp ?: return@produceState
value = state.value
// Use snapshotFlow and sample to prevent the value from changing too often.
snapshotFlow { state.value }
.sample(333L)
.collect {
value = it
}
}
HapticExtendedFloatingActionButton( HapticExtendedFloatingActionButton(
text = { text = {
Text( Text(
@@ -375,8 +395,7 @@ fun PatchesSelectorScreen(
contentDescription = stringResource(R.string.save) contentDescription = stringResource(R.string.save)
) )
}, },
expanded = patchLazyListStates.getOrNull(pagerState.currentPage)?.isScrollingUp expanded = expanded,
?: true,
onClick = { onClick = {
onSave(viewModel.getCustomSelection(), viewModel.getOptions()) onSave(viewModel.getCustomSelection(), viewModel.getOptions())
} }

View File

@@ -15,12 +15,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
@@ -50,6 +48,7 @@ import kotlinx.datetime.format.char
import kotlinx.datetime.toInstant import kotlinx.datetime.toInstant
import kotlinx.datetime.toLocalDateTime import kotlinx.datetime.toLocalDateTime
import java.util.Locale import java.util.Locale
import kotlin.math.abs
import kotlin.properties.PropertyDelegateProvider import kotlin.properties.PropertyDelegateProvider
import kotlin.properties.ReadWriteProperty import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
@@ -196,7 +195,12 @@ val transparentListItemColors
.also { transparentListItemColorsCached = it } .also { transparentListItemColorsCached = it }
@Composable @Composable
fun <T> EventEffect(flow: Flow<T>, vararg keys: Any?, state: Lifecycle.State = Lifecycle.State.STARTED, block: suspend (T) -> Unit) { fun <T> EventEffect(
flow: Flow<T>,
vararg keys: Any?,
state: Lifecycle.State = Lifecycle.State.STARTED,
block: suspend (T) -> Unit
) {
val lifecycleOwner = LocalLifecycleOwner.current val lifecycleOwner = LocalLifecycleOwner.current
val currentBlock by rememberUpdatedState(block) val currentBlock by rememberUpdatedState(block)
@@ -212,40 +216,36 @@ fun <T> EventEffect(flow: Flow<T>, vararg keys: Any?, state: Lifecycle.State = L
const val isScrollingUpSensitivity = 10 const val isScrollingUpSensitivity = 10
@Composable @Composable
fun LazyListState.isScrollingUp(): State<Boolean> { fun LazyListState.isScrollingUp() = produceState(true, this) {
return remember(this) { var previousIndex = firstVisibleItemIndex
var previousIndex by mutableIntStateOf(firstVisibleItemIndex) var previousScrollOffset = firstVisibleItemScrollOffset
var previousScrollOffset by mutableIntStateOf(firstVisibleItemScrollOffset)
derivedStateOf { snapshotFlow {
val indexChanged = previousIndex != firstVisibleItemIndex firstVisibleItemIndex to firstVisibleItemScrollOffset
val offsetChanged = }.collect { (index, scrollOffset) ->
kotlin.math.abs(previousScrollOffset - firstVisibleItemScrollOffset) > isScrollingUpSensitivity val indexChanged = previousIndex != index
val offsetChanged = abs(previousScrollOffset - scrollOffset) > isScrollingUpSensitivity
if (indexChanged) { value = when {
previousIndex > firstVisibleItemIndex indexChanged -> previousIndex > index
} else if (offsetChanged) { offsetChanged -> previousScrollOffset > scrollOffset
previousScrollOffset > firstVisibleItemScrollOffset else -> value
} else {
true
}.also {
previousIndex = firstVisibleItemIndex
previousScrollOffset = firstVisibleItemScrollOffset
}
} }
previousIndex = index
previousScrollOffset = scrollOffset
} }
} }
// TODO: support sensitivity
@Composable @Composable
fun ScrollState.isScrollingUp(): State<Boolean> { fun ScrollState.isScrollingUp() = produceState(true, this) {
return remember(this) { var previousScrollOffset = this@isScrollingUp.value
var previousScrollOffset by mutableIntStateOf(value)
derivedStateOf { snapshotFlow { this@isScrollingUp.value }.collect { scrollOffset ->
(previousScrollOffset >= value).also { if (abs(previousScrollOffset - scrollOffset) > isScrollingUpSensitivity) {
previousScrollOffset = value value = previousScrollOffset >= scrollOffset
}
} }
previousScrollOffset = scrollOffset
} }
} }