mirror of
https://github.com/atsunatsu/Look4Sat.git
synced 2026-10-06 20:47:22 +00:00
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:
1 parent
c89e79e28f
commit
b47bd78ecd
3 files changed
+48
-13
No files matched your search
@@ -81,6 +81,7 @@ import com.rtbishop.look4sat.core.presentation.Screen
|
|||||||
import com.rtbishop.look4sat.core.presentation.hasEnoughHeight
|
import com.rtbishop.look4sat.core.presentation.hasEnoughHeight
|
||||||
import com.rtbishop.look4sat.core.presentation.hasEnoughWidth
|
import com.rtbishop.look4sat.core.presentation.hasEnoughWidth
|
||||||
import com.rtbishop.look4sat.feature.map.MapDestination
|
import com.rtbishop.look4sat.feature.map.MapDestination
|
||||||
|
import com.rtbishop.look4sat.feature.map.MapFilterViewModel
|
||||||
import com.rtbishop.look4sat.feature.mutual.MutualScreen
|
import com.rtbishop.look4sat.feature.mutual.MutualScreen
|
||||||
import com.rtbishop.look4sat.feature.mutual.MutualViewModel
|
import com.rtbishop.look4sat.feature.mutual.MutualViewModel
|
||||||
import com.rtbishop.look4sat.feature.passes.PassesDestination
|
import com.rtbishop.look4sat.feature.passes.PassesDestination
|
||||||
@@ -169,6 +170,12 @@ fun MainScreen(
|
|||||||
viewModelStoreOwner = context as ViewModelStoreOwner,
|
viewModelStoreOwner = context as ViewModelStoreOwner,
|
||||||
factory = MutualViewModel.factory(container)
|
factory = MutualViewModel.factory(container)
|
||||||
)
|
)
|
||||||
|
// Activity-scoped so the map's award filter survives page switches;
|
||||||
|
// resets to VUCC only on cold start (fresh process).
|
||||||
|
val mapFilterViewModel: MapFilterViewModel = viewModel(
|
||||||
|
viewModelStoreOwner = context as ViewModelStoreOwner,
|
||||||
|
factory = MapFilterViewModel.factory()
|
||||||
|
)
|
||||||
|
|
||||||
CompositionLocalProvider(
|
CompositionLocalProvider(
|
||||||
LocalElevationThresholds provides ElevationThresholds(
|
LocalElevationThresholds provides ElevationThresholds(
|
||||||
@@ -237,7 +244,7 @@ fun MainScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
entry<Screen.Map> {
|
entry<Screen.Map> {
|
||||||
MapDestination()
|
MapDestination(mapFilterViewModel = mapFilterViewModel)
|
||||||
}
|
}
|
||||||
entry<Screen.Mutual> {
|
entry<Screen.Mutual> {
|
||||||
MutualScreen(
|
MutualScreen(
|
||||||
|
|||||||
@@ -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
|
@Composable
|
||||||
fun MapDestination() {
|
fun MapDestination(
|
||||||
|
mapFilterViewModel: MapFilterViewModel
|
||||||
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val container = (context.applicationContext as IContainerProvider).getMainContainer()
|
val container = (context.applicationContext as IContainerProvider).getMainContainer()
|
||||||
val viewModel: MapViewModel = viewModel(factory = MapViewModel.factory(container))
|
val viewModel: MapViewModel = viewModel(factory = MapViewModel.factory(container))
|
||||||
@@ -165,11 +167,16 @@ fun MapDestination() {
|
|||||||
viewModel.onAction(MapAction.SetVisible(false))
|
viewModel.onAction(MapAction.SetVisible(false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MapScreen(uiState, viewModel::onAction, mapView)
|
MapScreen(uiState, viewModel::onAction, mapView, mapFilterViewModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@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 rotateMod = Modifier.rotate(180f)
|
||||||
val timeString = uiState.mapData?.aosTime ?: "00:00:00"
|
val timeString = uiState.mapData?.aosTime ?: "00:00:00"
|
||||||
val isTimeAos = uiState.mapData?.isTimeAos ?: true
|
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
|
// Tapped worked grid -> centered QSO dialog. Local UI state: the map is the
|
||||||
// only consumer and it resets when leaving the page.
|
// only consumer and it resets when leaving the page.
|
||||||
var selectedGrid by remember { mutableStateOf<String?>(null) }
|
var selectedGrid by remember { mutableStateOf<String?>(null) }
|
||||||
// Selected award filter. Entering grid mode defaults to VUCC (the plain
|
// Selected award filter. Lives in an Activity-scoped ViewModel so it
|
||||||
// worked-grid view); the reset effect below re-asserts that on every entry.
|
// survives page switches; defaults to VUCC only once per process (cold start).
|
||||||
var selectedAward by remember { mutableStateOf<AwardType?>(AwardType.VUCC) }
|
var selectedAward by mapFilterViewModel.selectedAward
|
||||||
// Six-award progress derived from the confirmed QSO store; recomputed when
|
// Six-award progress derived from the confirmed QSO store; recomputed when
|
||||||
// the store changes (LoTW/Wavelog sync).
|
// the store changes (LoTW/Wavelog sync).
|
||||||
// Per operated-grid VUCC breakdown: myGrid -> set of worked grids worked from it.
|
// 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.overlays.getOrNull(OVERLAY_GRID) as? MaidenheadGridOverlay)?.selectedGrid = selectedGrid
|
||||||
mapView.invalidate()
|
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) {
|
LaunchedEffect(uiState.track) {
|
||||||
// In grid mode the map is centered on the local grid square; following
|
// In grid mode the map is centered on the local grid square; following
|
||||||
// the satellite subpoint here would override that centering on every
|
// the satellite subpoint here would override that centering on every
|
||||||
|
|||||||
Reference in new issue
Block a user