mirror of
https://github.com/atsunatsu/Look4Sat.git
synced 2026-10-05 20:17:29 +00:00
fix(map): scope first-call labels to the selected operated grid (台址)
This commit is contained in:
1 parent
7589f3f37d
commit
a459cfb046
3 files changed
+49
-7
No files matched your search
@@ -131,3 +131,13 @@ fun GridQso.stationGridSetKey(): String? =
|
|||||||
*/
|
*/
|
||||||
fun List<GridQso>.scopedToStation(stationId: String?): List<GridQso> =
|
fun List<GridQso>.scopedToStation(stationId: String?): List<GridQso> =
|
||||||
if (stationId == null) this else filter { it.stationGridSetKey() == stationId }
|
if (stationId == null) this else filter { it.stationGridSetKey() == stationId }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For every grid in the store, the callsign of the earliest QSO — scoped to the
|
||||||
|
* operated-grid selector's choice first, so a specific 台址 labels each worked
|
||||||
|
* cell with ITS first contact; null ("All") keeps the global first call.
|
||||||
|
*/
|
||||||
|
fun Map<String, List<GridQso>>.firstCallsByGrid(stationId: String?): Map<String, String> =
|
||||||
|
mapNotNull { (grid, qsos) ->
|
||||||
|
qsos.scopedToStation(stationId).minByOrNull { it.epochMs }?.let { grid to it.call }
|
||||||
|
}.toMap()
|
||||||
+31
@@ -94,4 +94,35 @@ class GridQsoStationScopeTest {
|
|||||||
val list = listOf(qso("A", myGrid = "OL62"))
|
val list = listOf(qso("A", myGrid = "OL62"))
|
||||||
assertEquals(emptyList<GridQso>(), list.scopedToStation("PM01"))
|
assertEquals(emptyList<GridQso>(), list.scopedToStation("PM01"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- firstCallsByGrid ---
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `first call per grid picks the earliest of the scoped records`() {
|
||||||
|
val store = mapOf(
|
||||||
|
"OL62" to listOf(
|
||||||
|
qso("OTHER", myGrid = "PM01").copy(epochMs = 10),
|
||||||
|
qso("MINE", myGrid = "OL62").copy(epochMs = 20)
|
||||||
|
),
|
||||||
|
"PM95" to listOf(qso("MINE2", myGrid = "OL62").copy(epochMs = 30))
|
||||||
|
)
|
||||||
|
assertEquals(mapOf("OL62" to "MINE", "PM95" to "MINE2"), store.firstCallsByGrid("OL62"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `first call per grid with All uses every record`() {
|
||||||
|
val store = mapOf(
|
||||||
|
"OL62" to listOf(
|
||||||
|
qso("OTHER", myGrid = "PM01").copy(epochMs = 10),
|
||||||
|
qso("MINE", myGrid = "OL62").copy(epochMs = 20)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assertEquals(mapOf("OL62" to "OTHER"), store.firstCallsByGrid(null))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `grids without scoped records get no first call`() {
|
||||||
|
val store = mapOf("OL62" to listOf(qso("A", myGrid = "PM01")))
|
||||||
|
assertEquals(emptyMap<String, String>(), store.firstCallsByGrid("OL62"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -85,6 +85,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
|||||||
import com.rtbishop.look4sat.core.domain.model.AwardCalculator
|
import com.rtbishop.look4sat.core.domain.model.AwardCalculator
|
||||||
import com.rtbishop.look4sat.core.domain.model.AwardProgress
|
import com.rtbishop.look4sat.core.domain.model.AwardProgress
|
||||||
import com.rtbishop.look4sat.core.domain.model.AwardType
|
import com.rtbishop.look4sat.core.domain.model.AwardType
|
||||||
|
import com.rtbishop.look4sat.core.domain.model.firstCallsByGrid
|
||||||
import com.rtbishop.look4sat.core.domain.model.MapSource
|
import com.rtbishop.look4sat.core.domain.model.MapSource
|
||||||
import com.rtbishop.look4sat.core.domain.model.myStationGrids
|
import com.rtbishop.look4sat.core.domain.model.myStationGrids
|
||||||
import com.rtbishop.look4sat.core.domain.model.scopedToStation
|
import com.rtbishop.look4sat.core.domain.model.scopedToStation
|
||||||
@@ -306,13 +307,6 @@ private fun MapScreen(
|
|||||||
// Selected award filter. Lives in an Activity-scoped ViewModel so it
|
// Selected award filter. Lives in an Activity-scoped ViewModel so it
|
||||||
// survives page switches; defaults to VUCC only once per process (cold start).
|
// survives page switches; defaults to VUCC only once per process (cold start).
|
||||||
var selectedAward by mapFilterViewModel.selectedAward
|
var selectedAward by mapFilterViewModel.selectedAward
|
||||||
// First callsign worked in each grid (earliest QSO by time), used by the
|
|
||||||
// "首通呼号" label mode — derived from the same QSO store as the worked fills.
|
|
||||||
val firstCallsByGrid: Map<String, String> = remember(uiState.workedGridQsos) {
|
|
||||||
uiState.workedGridQsos.mapNotNull { (grid, qsos) ->
|
|
||||||
qsos.minByOrNull { it.epochMs }?.let { grid to it.call }
|
|
||||||
}.toMap()
|
|
||||||
}
|
|
||||||
// 台址名单: QSOs grouped by the 台址's GRID SET. A station location (台址)
|
// 台址名单: QSOs grouped by the 台址's GRID SET. A station location (台址)
|
||||||
// can span several grids (MY_GRIDSQUARE + MY_VUCC_GRIDS), and QSOs whose
|
// can span several grids (MY_GRIDSQUARE + MY_VUCC_GRIDS), and QSOs whose
|
||||||
// location covers the same grid set belong to the same 台址; groups with an
|
// location covers the same grid set belong to the same 台址; groups with an
|
||||||
@@ -353,6 +347,13 @@ private fun MapScreen(
|
|||||||
if (selectedStationId == null) uiState.workedGrids
|
if (selectedStationId == null) uiState.workedGrids
|
||||||
else stationGroups.firstOrNull { it.id == selectedStationId }?.worked ?: emptySet()
|
else stationGroups.firstOrNull { it.id == selectedStationId }?.worked ?: emptySet()
|
||||||
}
|
}
|
||||||
|
// First callsign worked in each grid (earliest QSO by time), used by the
|
||||||
|
// "首通呼号" label mode. Scoped to the selected 台址 like the fills: with a
|
||||||
|
// specific operated grid chosen, the label must show ITS first contact, not
|
||||||
|
// another 台址's (user decision 2026-10-05); "All" = global first call.
|
||||||
|
val firstCallsByGrid: Map<String, String> = remember(uiState.workedGridQsos, selectedStationId) {
|
||||||
|
uiState.workedGridQsos.firstCallsByGrid(selectedStationId)
|
||||||
|
}
|
||||||
val isGridMode = uiState.isGridMode
|
val isGridMode = uiState.isGridMode
|
||||||
// True when this composition restored a saved viewport. Only grid mode
|
// True when this composition restored a saved viewport. Only grid mode
|
||||||
// restores the viewport the user left (satellite mode keeps its original
|
// restores the viewport the user left (satellite mode keeps its original
|
||||||
|
|||||||
Reference in new issue
Block a user