From dbb6745b7b4b7bb1fd07fa483822e26ac4dbae55 Mon Sep 17 00:00:00 2001 From: austinried <4966622+austinried@users.noreply.github.com> Date: Sun, 4 Oct 2026 00:21:37 +0000 Subject: [PATCH] address review: replay one dismissal so a recreated screen still sees it A notification tap can fire the dismissal before the route's collector subscribes (activity recreated with a restored options sheet), and a replay-zero broadcast drops it. Replaying one is a no-op on default state and the new ContextMenuHost test guards it. --- .../ui/components/ItemContextMenu.kt | 4 +++- .../com/subtracks/ui/library/LibraryScreen.kt | 1 + .../ui/components/ContextMenuHostTest.kt | 19 +++++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 app/src/test/kotlin/com/subtracks/ui/components/ContextMenuHostTest.kt diff --git a/app/src/main/kotlin/com/subtracks/ui/components/ItemContextMenu.kt b/app/src/main/kotlin/com/subtracks/ui/components/ItemContextMenu.kt index ecde6f85..41921066 100644 --- a/app/src/main/kotlin/com/subtracks/ui/components/ItemContextMenu.kt +++ b/app/src/main/kotlin/com/subtracks/ui/components/ItemContextMenu.kt @@ -151,7 +151,9 @@ class ContextMenuHost { var actions by mutableStateOf(ItemActions()) private set - private val transientDismissals = MutableSharedFlow(extraBufferCapacity = 1) + // Replay one so a screen recreated by the notification (whose collector subscribes after the + // event fires) still sees the dismissal; every onDismiss is a harmless no-op on default state. + private val transientDismissals = MutableSharedFlow(replay = 1) // Fires when an external event (opening now playing) should close menu-like UI the host does // not own, such as the library's list options sheet. diff --git a/app/src/main/kotlin/com/subtracks/ui/library/LibraryScreen.kt b/app/src/main/kotlin/com/subtracks/ui/library/LibraryScreen.kt index 2e53ffda..9b52f895 100644 --- a/app/src/main/kotlin/com/subtracks/ui/library/LibraryScreen.kt +++ b/app/src/main/kotlin/com/subtracks/ui/library/LibraryScreen.kt @@ -420,6 +420,7 @@ fun LibraryScreen( var searchActive by rememberSaveable { mutableStateOf(false) } val searchFocus = remember { FocusRequester() } + // Search mode is left open so the typed query survives; MainNavigation dismisses the keyboard. DismissOnRequest(dismissals) { showOptions = false } LaunchedEffect(pagerState) { diff --git a/app/src/test/kotlin/com/subtracks/ui/components/ContextMenuHostTest.kt b/app/src/test/kotlin/com/subtracks/ui/components/ContextMenuHostTest.kt new file mode 100644 index 00000000..3ea83d88 --- /dev/null +++ b/app/src/test/kotlin/com/subtracks/ui/components/ContextMenuHostTest.kt @@ -0,0 +1,19 @@ +package com.subtracks.ui.components + +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.test.runTest +import kotlinx.coroutines.withTimeoutOrNull +import org.junit.Assert.assertEquals +import org.junit.Test + +class ContextMenuHostTest { + @Test + fun aDismissalBeforeCollectionIsStillDelivered() = + runTest { + val host = ContextMenuHost() + + host.dismissTransients() + + assertEquals(Unit, withTimeoutOrNull(1_000) { host.dismissals.first() }) + } +}