From 5fb3cae92cb953098585cd38a92eab4ad478991d Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Thu, 6 Aug 2026 11:32:31 -0300 Subject: [PATCH] Fix back behavior after deselecting all items in the gallery. --- .../signal/mediasend/MediaSendFlowEvent.kt | 1 + .../mediasend/MediaSendFlowViewModel.kt | 19 +++++++++++++++++++ .../screens/select/MediaSelectScreen.kt | 7 +++++++ .../screens/select/MediaSelectScreenEvents.kt | 1 + .../screens/select/MediaSelectViewModel.kt | 1 + 5 files changed, 29 insertions(+) diff --git a/feature/media-send/src/main/java/org/signal/mediasend/MediaSendFlowEvent.kt b/feature/media-send/src/main/java/org/signal/mediasend/MediaSendFlowEvent.kt index 791dde91e0..ed5d3164f0 100644 --- a/feature/media-send/src/main/java/org/signal/mediasend/MediaSendFlowEvent.kt +++ b/feature/media-send/src/main/java/org/signal/mediasend/MediaSendFlowEvent.kt @@ -25,4 +25,5 @@ internal sealed interface MediaSendFlowEvent { data class NavigateToFiles(val mediaFolder: MediaFolder) : MediaSendFlowEvent data object NavigateToEdit : MediaSendFlowEvent data object NavigateToCamera : MediaSendFlowEvent + data object NavigateBack : MediaSendFlowEvent } diff --git a/feature/media-send/src/main/java/org/signal/mediasend/MediaSendFlowViewModel.kt b/feature/media-send/src/main/java/org/signal/mediasend/MediaSendFlowViewModel.kt index 2526dda914..6b3fba1063 100644 --- a/feature/media-send/src/main/java/org/signal/mediasend/MediaSendFlowViewModel.kt +++ b/feature/media-send/src/main/java/org/signal/mediasend/MediaSendFlowViewModel.kt @@ -256,6 +256,25 @@ class MediaSendFlowViewModel( is MediaSendFlowEvent.NavigateToFiles -> backStack.goToFiles(event.mediaFolder) MediaSendFlowEvent.NavigateToEdit -> backStack.goToEdit() MediaSendFlowEvent.NavigateToCamera -> backStack.goToCamera() + MediaSendFlowEvent.NavigateBack -> onPopFromSelect() + } + } + + /** + * Backs out of a select screen while nothing is selected, stepping over an editor that would have nothing to edit + * and no toolbar to leave by. Decided here rather than when the selection empties, so that re-selecting something + * still returns the user to their editor. + */ + private fun onPopFromSelect() { + val destination = backStack.dropLast(1).dropLastWhile { it == MediaSendRoute.Edit } + + if (destination.isEmpty()) { + sendHudCommand(MediaSendFlowHudCommand.CloseScreen) + return + } + + while (backStack.size > destination.size) { + backStack.pop() } } diff --git a/feature/media-send/src/main/java/org/signal/mediasend/screens/select/MediaSelectScreen.kt b/feature/media-send/src/main/java/org/signal/mediasend/screens/select/MediaSelectScreen.kt index 8a566d1e7f..f1d6a42d4f 100644 --- a/feature/media-send/src/main/java/org/signal/mediasend/screens/select/MediaSelectScreen.kt +++ b/feature/media-send/src/main/java/org/signal/mediasend/screens/select/MediaSelectScreen.kt @@ -5,6 +5,7 @@ package org.signal.mediasend.screens.select +import androidx.activity.compose.BackHandler import androidx.activity.compose.LocalOnBackPressedDispatcherOwner import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.animateDpAsState @@ -129,6 +130,12 @@ internal fun MediaSelectScreen( val gridState = rememberLazyGridState() val dragToSelectState = rememberDragToSelectMediaState(state, onEvent, gridState) + // Only an empty selection can leave an editor with nothing to edit behind us. Every other back press is left to the + // navigation default, which keeps its predictive-back gesture. + BackHandler(enabled = state.selectedMedia.isEmpty()) { + onEvent(MediaSelectScreenEvents.NavigateBack) + } + // Once the selection starts refusing items there is nothing left for the drag to do, and letting it run on would // raise a refusal for every further tile it crosses. LaunchedEffect(state.isSelectionRejected) { diff --git a/feature/media-send/src/main/java/org/signal/mediasend/screens/select/MediaSelectScreenEvents.kt b/feature/media-send/src/main/java/org/signal/mediasend/screens/select/MediaSelectScreenEvents.kt index 5ad4ec116c..bf2d7e908b 100644 --- a/feature/media-send/src/main/java/org/signal/mediasend/screens/select/MediaSelectScreenEvents.kt +++ b/feature/media-send/src/main/java/org/signal/mediasend/screens/select/MediaSelectScreenEvents.kt @@ -34,6 +34,7 @@ sealed interface MediaSelectScreenEvents { data class ReorderSelectedMedia(val fromIndex: Int, val toIndex: Int) : MediaSelectScreenEvents data object NavigateToEdit : MediaSelectScreenEvents data object NavigateToCamera : MediaSelectScreenEvents + data object NavigateBack : MediaSelectScreenEvents /** Re-read the gallery and the current permission level, e.g. after coming back from app settings. */ data object Refresh : MediaSelectScreenEvents diff --git a/feature/media-send/src/main/java/org/signal/mediasend/screens/select/MediaSelectViewModel.kt b/feature/media-send/src/main/java/org/signal/mediasend/screens/select/MediaSelectViewModel.kt index 7b8d8b78d0..0a10db103a 100644 --- a/feature/media-send/src/main/java/org/signal/mediasend/screens/select/MediaSelectViewModel.kt +++ b/feature/media-send/src/main/java/org/signal/mediasend/screens/select/MediaSelectViewModel.kt @@ -92,6 +92,7 @@ internal class MediaSelectViewModel( is MediaSelectScreenEvents.ReorderSelectedMedia -> parentEventEmitter(MediaSendFlowEvent.ReorderSelectedMedia(event.fromIndex, event.toIndex)) MediaSelectScreenEvents.NavigateToEdit -> parentEventEmitter(MediaSendFlowEvent.NavigateToEdit) MediaSelectScreenEvents.NavigateToCamera -> parentEventEmitter(MediaSendFlowEvent.NavigateToCamera) + MediaSelectScreenEvents.NavigateBack -> parentEventEmitter(MediaSendFlowEvent.NavigateBack) MediaSelectScreenEvents.Refresh -> refresh() MediaSelectScreenEvents.RequestMediaPermissions -> requestReadMediaPermissions(reportDenial = true) MediaSelectScreenEvents.SelectMorePhotos -> requestReadMediaPermissions(reportDenial = false)