fix(mutual,passes): map Match prefill opens at time-range card; whats-new dialog scrollable

- Match prefill now positions the LazyColumn via its initial state
  (queryMutualPasses(initialScrollIndex = 1)) instead of a post-layout
  scrollToItem, so the page reliably opens with the time-range card at the
  top while the 24h query runs.
- The install-time what's new dialog text now scrolls inside a bounded box
  (max 360dp) instead of overflowing the fixed dialog frame.
This commit is contained in:
atsunatsu committed 2026-09-22 11:05:47 +08:00
1 parent 1c6388f3bc
commit d5c100903b
3 files changed
+21 -30

No files matched your search

@@ -369,17 +369,6 @@ private fun MutualContent(
)
}
}
// Prefill from the map's grid-QSO dialog "Match" button: jump straight to
// the time-range card. Index accounts for the optional error card at the
// top (error = 0, station inputs = 1, time range = 2; else 1).
val matchSearchIndex = if (state.errorMessage != null) 2 else 1
LaunchedEffect(state.scrollToTimeRange, matchSearchIndex) {
if (state.scrollToTimeRange) {
listState.scrollToItem(matchSearchIndex)
viewModel.consumeScrollToTimeRange()
}
}
}
@Composable
@@ -55,11 +55,7 @@ data class MutualUiState(
val hasSearched: Boolean = false,
val selectedPassIndex: Int = -1,
val isUtc: Boolean = false,
val errorMessage: String? = null,
// One-shot flag set by prefillMatchFromGrid() (map grid → Match button):
// the page scrolls to the time-range card, then consumeScrollToTimeRange()
// clears it.
val scrollToTimeRange: Boolean = false
val errorMessage: String? = null
)
class MutualViewModel(
@@ -190,9 +186,10 @@ class MutualViewModel(
/**
* Pre-fill the match page for a target grid picked from the map's grid-QSO
* dialog ("Match" button): set the opposite-station grid (with its
* coordinates), reset the time range to 24h, flag the page to scroll
* straight to the time-range card, and start the query immediately so
* results are ready when the page opens.
* coordinates), reset the time range to 24h, start the query immediately
* so results are ready when the page opens, and position the list at the
* time-range card (LazyColumn item 1, the card right below the station
* inputs) so the page opens showing it at the top.
*/
fun prefillMatchFromGrid(grid: String) {
val g = grid.trim().uppercase()
@@ -202,18 +199,13 @@ class MutualViewModel(
stationBGrid = g,
stationBLat = pos?.let { p -> "%.4f".format(p.latitude) } ?: it.stationBLat,
stationBLon = pos?.let { p -> "%.4f".format(p.longitude) } ?: it.stationBLon,
hoursAhead = 24,
scrollToTimeRange = true
hoursAhead = 24
)
}
queryMutualPasses()
queryMutualPasses(initialScrollIndex = 1)
}
fun consumeScrollToTimeRange() {
_uiState.update { it.copy(scrollToTimeRange = false) }
}
fun queryMutualPasses() {
fun queryMutualPasses(initialScrollIndex: Int = 0) {
val state = _uiState.value
// Resolve positions from lat/lon or grid
@@ -241,8 +233,10 @@ class MutualViewModel(
)
}
// The results list is about to be replaced, so the scroll position must
// not leak from the previous query's list.
listScrollIndex = 0
// not leak from the previous query's list. A prefill from the map
// (grid-QSO dialog "Match" button) instead starts at the time-range
// card so the page opens with it at the top.
listScrollIndex = initialScrollIndex
listScrollOffset = 0
queryGeneration += 1
@@ -30,12 +30,15 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
@@ -145,11 +148,16 @@ private fun PassesScreen(
onAccept = dismiss,
titleFontSize = 18
) { padding ->
// The changelog is long; keep the dialog frame fixed and let the
// text scroll inside it (matches the update-checker page pattern).
Text(
text = stringResource(R.string.pass_whatsnew_message),
fontSize = 16.sp,
color = MaterialTheme.colorScheme.onSurface,
modifier = Modifier.padding(horizontal = padding)
modifier = Modifier
.padding(horizontal = padding)
.heightIn(max = 360.dp)
.verticalScroll(rememberScrollState())
)
}
}