feat(map): keep award filter selection across page switches

The award chip (VUCC/DXCC/WAJA/...) now lives in an Activity-scoped
MapFilterViewModel instead of a per-composition remember, so switching
to another page and back keeps the last selection; it resets to VUCC
only once per process (cold start). Removes the effect that forced
VUCC on every grid-mode entry.
This commit is contained in:
atsunatsu committed 2026-09-17 12:57:36 +08:00
1 parent c89e79e28f
commit b47bd78ecd
3 files changed
+48 -13

No files matched your search

@@ -0,0 +1,27 @@
package com.rtbishop.look4sat.feature.map
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.rtbishop.look4sat.core.domain.model.AwardType
/**
* Session-scoped holder for the map's award filter selection.
*
* Scoped to the Activity (not the navigation entry) so the selection survives
* switching to another page and back; it resets to VUCC only when the process
* starts fresh (cold start), which is what the user expects as the default.
*/
class MapFilterViewModel : ViewModel() {
/** Last chosen award chip; null means "All". Initialized to VUCC once per process. */
val selectedAward: MutableState<AwardType?> = mutableStateOf(AwardType.VUCC)
companion object {
fun factory() = viewModelFactory {
initializer { MapFilterViewModel() }
}
}
}
@@ -144,7 +144,9 @@ private val moonIconPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
}
@Composable
fun MapDestination() {
fun MapDestination(
mapFilterViewModel: MapFilterViewModel
) {
val context = LocalContext.current
val container = (context.applicationContext as IContainerProvider).getMainContainer()
val viewModel: MapViewModel = viewModel(factory = MapViewModel.factory(container))
@@ -165,11 +167,16 @@ fun MapDestination() {
viewModel.onAction(MapAction.SetVisible(false))
}
}
MapScreen(uiState, viewModel::onAction, mapView)
MapScreen(uiState, viewModel::onAction, mapView, mapFilterViewModel)
}
@Composable
private fun MapScreen(uiState: MapState, onAction: (MapAction) -> Unit, mapView: MapView) {
private fun MapScreen(
uiState: MapState,
onAction: (MapAction) -> Unit,
mapView: MapView,
mapFilterViewModel: MapFilterViewModel
) {
val rotateMod = Modifier.rotate(180f)
val timeString = uiState.mapData?.aosTime ?: "00:00:00"
val isTimeAos = uiState.mapData?.isTimeAos ?: true
@@ -177,9 +184,9 @@ private fun MapScreen(uiState: MapState, onAction: (MapAction) -> Unit, mapView:
// Tapped worked grid -> centered QSO dialog. Local UI state: the map is the
// only consumer and it resets when leaving the page.
var selectedGrid by remember { mutableStateOf<String?>(null) }
// Selected award filter. Entering grid mode defaults to VUCC (the plain
// worked-grid view); the reset effect below re-asserts that on every entry.
var selectedAward by remember { mutableStateOf<AwardType?>(AwardType.VUCC) }
// Selected award filter. Lives in an Activity-scoped ViewModel so it
// survives page switches; defaults to VUCC only once per process (cold start).
var selectedAward by mapFilterViewModel.selectedAward
// Six-award progress derived from the confirmed QSO store; recomputed when
// the store changes (LoTW/Wavelog sync).
// Per operated-grid VUCC breakdown: myGrid -> set of worked grids worked from it.
@@ -240,12 +247,6 @@ private fun MapScreen(uiState: MapState, onAction: (MapAction) -> Unit, mapView:
(mapView.overlays.getOrNull(OVERLAY_GRID) as? MaidenheadGridOverlay)?.selectedGrid = selectedGrid
mapView.invalidate()
}
// Re-assert the VUCC default each time grid mode is entered; while already
// in grid mode the user's chip choice is preserved.
LaunchedEffect(uiState.isGridMode) {
if (uiState.isGridMode) selectedAward = AwardType.VUCC
}
LaunchedEffect(uiState.track) {
// In grid mode the map is centered on the local grid square; following
// the satellite subpoint here would override that centering on every