From 4c0b6b02e95a8d6f655bcf5c25493b1f9a4a4dcd Mon Sep 17 00:00:00 2001 From: Ax333l Date: Fri, 9 Jan 2026 21:33:08 +0100 Subject: [PATCH] fix: Save FAB freaking out in select patches screen --- .../ui/screen/PatchesSelectorScreen.kt | 27 ++++++-- .../java/app/revanced/manager/util/Util.kt | 62 +++++++++---------- 2 files changed, 54 insertions(+), 35 deletions(-) diff --git a/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt index 284fa50b..e55ae439 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt @@ -49,10 +49,12 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier 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.isScrollingUp import app.revanced.manager.util.transparentListItemColors +import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.flow.sample import kotlinx.coroutines.launch -@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class) +@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class, FlowPreview::class) @Composable fun PatchesSelectorScreen( onSave: (PatchSelection?, Options) -> Unit, @@ -231,7 +235,8 @@ fun PatchesSelectorScreen( viewModel.selectionWarningEnabled -> showSelectionWarning = true // 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 else -> viewModel.togglePatch(uid, patch) @@ -360,6 +365,21 @@ fun PatchesSelectorScreen( ) { 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( text = { Text( @@ -375,8 +395,7 @@ fun PatchesSelectorScreen( contentDescription = stringResource(R.string.save) ) }, - expanded = patchLazyListStates.getOrNull(pagerState.currentPage)?.isScrollingUp - ?: true, + expanded = expanded, onClick = { onSave(viewModel.getCustomSelection(), viewModel.getOptions()) } diff --git a/app/src/main/java/app/revanced/manager/util/Util.kt b/app/src/main/java/app/revanced/manager/util/Util.kt index a2706976..cf840710 100644 --- a/app/src/main/java/app/revanced/manager/util/Util.kt +++ b/app/src/main/java/app/revanced/manager/util/Util.kt @@ -15,12 +15,10 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.State -import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.remember +import androidx.compose.runtime.produceState import androidx.compose.runtime.rememberUpdatedState -import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.graphics.Color @@ -50,6 +48,7 @@ import kotlinx.datetime.format.char import kotlinx.datetime.toInstant import kotlinx.datetime.toLocalDateTime import java.util.Locale +import kotlin.math.abs import kotlin.properties.PropertyDelegateProvider import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty @@ -196,7 +195,12 @@ val transparentListItemColors .also { transparentListItemColorsCached = it } @Composable -fun EventEffect(flow: Flow, vararg keys: Any?, state: Lifecycle.State = Lifecycle.State.STARTED, block: suspend (T) -> Unit) { +fun EventEffect( + flow: Flow, + vararg keys: Any?, + state: Lifecycle.State = Lifecycle.State.STARTED, + block: suspend (T) -> Unit +) { val lifecycleOwner = LocalLifecycleOwner.current val currentBlock by rememberUpdatedState(block) @@ -212,40 +216,36 @@ fun EventEffect(flow: Flow, vararg keys: Any?, state: Lifecycle.State = L const val isScrollingUpSensitivity = 10 @Composable -fun LazyListState.isScrollingUp(): State { - return remember(this) { - var previousIndex by mutableIntStateOf(firstVisibleItemIndex) - var previousScrollOffset by mutableIntStateOf(firstVisibleItemScrollOffset) +fun LazyListState.isScrollingUp() = produceState(true, this) { + var previousIndex = firstVisibleItemIndex + var previousScrollOffset = firstVisibleItemScrollOffset - derivedStateOf { - val indexChanged = previousIndex != firstVisibleItemIndex - val offsetChanged = - kotlin.math.abs(previousScrollOffset - firstVisibleItemScrollOffset) > isScrollingUpSensitivity + snapshotFlow { + firstVisibleItemIndex to firstVisibleItemScrollOffset + }.collect { (index, scrollOffset) -> + val indexChanged = previousIndex != index + val offsetChanged = abs(previousScrollOffset - scrollOffset) > isScrollingUpSensitivity - if (indexChanged) { - previousIndex > firstVisibleItemIndex - } else if (offsetChanged) { - previousScrollOffset > firstVisibleItemScrollOffset - } else { - true - }.also { - previousIndex = firstVisibleItemIndex - previousScrollOffset = firstVisibleItemScrollOffset - } + value = when { + indexChanged -> previousIndex > index + offsetChanged -> previousScrollOffset > scrollOffset + else -> value } + previousIndex = index + previousScrollOffset = scrollOffset } } -// TODO: support sensitivity @Composable -fun ScrollState.isScrollingUp(): State { - return remember(this) { - var previousScrollOffset by mutableIntStateOf(value) - derivedStateOf { - (previousScrollOffset >= value).also { - previousScrollOffset = value - } +fun ScrollState.isScrollingUp() = produceState(true, this) { + var previousScrollOffset = this@isScrollingUp.value + + snapshotFlow { this@isScrollingUp.value }.collect { scrollOffset -> + if (abs(previousScrollOffset - scrollOffset) > isScrollingUpSensitivity) { + value = previousScrollOffset >= scrollOffset } + + previousScrollOffset = scrollOffset } }