diff --git a/android/src/androidTest/java/com/inspiredandroid/linuxcommandbibliotheca/ComposeDeeplinkTests.kt b/android/src/androidTest/java/com/inspiredandroid/linuxcommandbibliotheca/ComposeDeeplinkTests.kt index 455d50e5a..244717852 100644 --- a/android/src/androidTest/java/com/inspiredandroid/linuxcommandbibliotheca/ComposeDeeplinkTests.kt +++ b/android/src/androidTest/java/com/inspiredandroid/linuxcommandbibliotheca/ComposeDeeplinkTests.kt @@ -3,12 +3,26 @@ package com.inspiredandroid.linuxcommandbibliotheca import android.content.Context import android.content.Intent import android.net.Uri +import androidx.activity.compose.setContent +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.remember +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalWindowInfo +import androidx.compose.ui.platform.WindowInfo +import androidx.compose.ui.test.assertCountEquals +import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.assertTextEquals import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.onAllNodesWithContentDescription +import androidx.compose.ui.test.onAllNodesWithText import androidx.compose.ui.test.onNodeWithContentDescription +import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import androidx.compose.ui.unit.IntSize +import androidx.compose.ui.unit.dp import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.linuxcommandlibrary.app.App import org.junit.Before import org.junit.Rule import org.junit.Test @@ -77,6 +91,36 @@ class ComposeDeeplinkTests { .assertTextEquals("Commands") } + @Test + fun testCommandListDoesNotSelectFirstCommandOnExpandedLayout() { + // Pretend the window is tablet sized so the two-pane list/detail layout is used. + composeTestRule.runOnUiThread { + composeTestRule.activity.setContent { + val windowInfo = LocalWindowInfo.current + val density = LocalDensity.current + val expandedWindowInfo = remember(windowInfo, density) { + object : WindowInfo by windowInfo { + override val containerSize: IntSize + get() = with(density) { IntSize(1280.dp.roundToPx(), 800.dp.roundToPx()) } + } + } + CompositionLocalProvider(LocalWindowInfo provides expandedWindowInfo) { + App(initialDeeplink = "https://linuxcommandlibrary.com/") + } + } + } + + // Wait for the command list to load before checking that nothing got selected. + composeTestRule.waitUntil(timeoutMillis = 10_000) { + composeTestRule.onAllNodesWithText("2048").fetchSemanticsNodes().isNotEmpty() + } + composeTestRule.waitForIdle() + + composeTestRule.onNodeWithText("Select a command").assertIsDisplayed() + composeTestRule.onAllNodesWithContentDescription("Search in page") + .assertCountEquals(0) + } + @Test fun testCommandDetail() { openUrl("https://linuxcommandlibrary.com/man/2048") diff --git a/composeApp/src/commonMain/kotlin/com/linuxcommandlibrary/app/LinuxNavGraph.kt b/composeApp/src/commonMain/kotlin/com/linuxcommandlibrary/app/LinuxNavGraph.kt index b3523008c..b645098c1 100644 --- a/composeApp/src/commonMain/kotlin/com/linuxcommandlibrary/app/LinuxNavGraph.kt +++ b/composeApp/src/commonMain/kotlin/com/linuxcommandlibrary/app/LinuxNavGraph.kt @@ -84,7 +84,6 @@ private fun CommandsTab(state: LinuxNavState, withTabStackOverlay: Boolean) { onNavigate = state::onNavigate, stack = commandsStack, onPopStack = { state.popStack(RouteKey.Commands) }, - isExpanded = !withTabStackOverlay, ) if (withTabStackOverlay) { TabStackTop( diff --git a/composeApp/src/commonMain/kotlin/com/linuxcommandlibrary/app/ui/screens/commands/CommandsPaneScreen.kt b/composeApp/src/commonMain/kotlin/com/linuxcommandlibrary/app/ui/screens/commands/CommandsPaneScreen.kt index b1fefda94..5335a6f7d 100644 --- a/composeApp/src/commonMain/kotlin/com/linuxcommandlibrary/app/ui/screens/commands/CommandsPaneScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/linuxcommandlibrary/app/ui/screens/commands/CommandsPaneScreen.kt @@ -36,7 +36,6 @@ import com.linuxcommandlibrary.app.ui.screens.commandlist.CommandListViewModel import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableSet import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import org.koin.compose.koinInject @@ -51,7 +50,6 @@ internal fun CommandsPaneScreen( onNavigate: (NavEvent) -> Unit, stack: SnapshotStateList, onPopStack: () -> Unit, - isExpanded: Boolean, ) { val listViewModel: CommandListViewModel = koinInject() val commands by listViewModel.commands.collectAsState() @@ -63,20 +61,6 @@ internal fun CommandsPaneScreen( onSelectionConsumed() } - // Two-pane layouts: pre-select the first command once data loads so the detail - // pane shows real content instead of an empty placeholder. Compact (single-pane) - // skips this — auto-pushing to detail on launch would be jarring. - LaunchedEffect(isExpanded) { - if (!isExpanded) return@LaunchedEffect - val list = listViewModel.commands.first { it.isNotEmpty() } - if (navigator.currentDestination?.contentKey == null && - stack.isEmpty() && - pendingSelection == null - ) { - navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, list.first().name) - } - } - // Hoisted so the LazyColumn keeps its scroll position across AnimatedPane's // exit/enter cycle. Without hoisting, the pane content's nested rememberSaveable was // intermittently coerced to 0 on re-mount because the bookmark-driven sort re-emits