diff --git a/core/data/src/main/java/com/rtbishop/look4sat/core/data/repository/SelectionRepo.kt b/core/data/src/main/java/com/rtbishop/look4sat/core/data/repository/SelectionRepo.kt index 7fc8f005..19101d4e 100644 --- a/core/data/src/main/java/com/rtbishop/look4sat/core/data/repository/SelectionRepo.kt +++ b/core/data/src/main/java/com/rtbishop/look4sat/core/data/repository/SelectionRepo.kt @@ -102,7 +102,10 @@ class SelectionRepo( override fun getTypesList() = buildList { // 三个转发器/活动虚拟类型排在最前. addAll(Sources.virtualTypeNames) - addAll(Sources.satelliteDataUrls.keys.sorted().toMutableList().apply { removeAt(0) }) + // 所有真实 TLE 源类型均可选(含 "All" = CelesTrak active);只排除 + // "Other"(空 URL 占位,无数据)。不要用 removeAt(0) —— 字母序第一个 + // 是 "All"/"ARISS" 这类真实源,删掉会让用户勾不到它们。 + addAll(Sources.satelliteDataUrls.keys.sorted().filterNot { it == "Other" }) } override suspend fun getEntriesFlow() = withContext(dispatcher) { diff --git a/core/data/src/main/java/com/rtbishop/look4sat/core/data/repository/SettingsRepo.kt b/core/data/src/main/java/com/rtbishop/look4sat/core/data/repository/SettingsRepo.kt index f4cafde3..b90f290b 100644 --- a/core/data/src/main/java/com/rtbishop/look4sat/core/data/repository/SettingsRepo.kt +++ b/core/data/src/main/java/com/rtbishop/look4sat/core/data/repository/SettingsRepo.kt @@ -498,7 +498,9 @@ class SettingsRepo( } override fun setSatelliteTypeIds(type: String, ids: List) { - if (type == "All") return + // "All" is a real TLE source type (CelesTrak active group) whose ids + // must be persisted like any other type — skipping it made the "All" + // filter resolve to an empty set and show nothing. val typesString = ids.joinToString(separatorComma) preferences.edit { putString("type$type", typesString) } } diff --git a/core/data/src/test/java/com/rtbishop/look4sat/core/data/repository/DatabaseRepoTest.kt b/core/data/src/test/java/com/rtbishop/look4sat/core/data/repository/DatabaseRepoTest.kt index 38475503..14eac89c 100644 --- a/core/data/src/test/java/com/rtbishop/look4sat/core/data/repository/DatabaseRepoTest.kt +++ b/core/data/src/test/java/com/rtbishop/look4sat/core/data/repository/DatabaseRepoTest.kt @@ -127,6 +127,29 @@ class DatabaseRepoTest { assertEquals(listOf(98248), settingsRepo.satelliteTypeIdsByType["SatNOGS"]) } + @Test + fun `remote update persists All source type ids`() = runTest(dispatcher) { + // Regression: "All" (CelesTrak active) is a real TLE source type whose + // ids must be persisted on sync — the old `if (type == "All") return` + // silently dropped them, making the "All" filter resolve to nothing. + val allUrl = Sources.satelliteDataUrls.getValue("All") + val localSource = FakeLocalSource() + val remoteSource = FakeRemoteSource().apply { + networkStreams[allUrl] = { validCsvStream() } + } + val settingsRepo = FakeSettingsRepo( + dataSources = DataSourcesSettings( + satelliteUrls = listOf(allUrl), + transceiversUrls = emptyList() + ) + ) + val repository = DatabaseRepo(dispatcher, dataParser, localSource, remoteSource, settingsRepo) + + repository.updateFromRemote() + + assertEquals(listOf(25544), settingsRepo.satelliteTypeIdsByType["All"]) + } + private fun validCsvStream(): InputStream = """ OBJECT_NAME,OBJECT_ID,EPOCH,MEAN_MOTION,ECCENTRICITY,INCLINATION,RA_OF_ASC_NODE,ARG_OF_PERICENTER,MEAN_ANOMALY,EPHEMERIS_TYPE,CLASSIFICATION_TYPE,NORAD_CAT_ID,ELEMENT_SET_NO,REV_AT_EPOCH,BSTAR,MEAN_MOTION_DOT,MEAN_MOTION_DDOT ISS (ZARYA),1998-067A,2021-11-16T12:28:09.322176,15.48582035,.0004694,51.6447,309.4881,203.6966,299.8876,0,U,25544,999,31220,.31985E-4,.1288E-4,0 diff --git a/core/data/src/test/java/com/rtbishop/look4sat/core/data/repository/SelectionRepoSearchTest.kt b/core/data/src/test/java/com/rtbishop/look4sat/core/data/repository/SelectionRepoSearchTest.kt index 6cead0d9..1d5c0177 100644 --- a/core/data/src/test/java/com/rtbishop/look4sat/core/data/repository/SelectionRepoSearchTest.kt +++ b/core/data/src/test/java/com/rtbishop/look4sat/core/data/repository/SelectionRepoSearchTest.kt @@ -217,6 +217,45 @@ class SelectionRepoSearchTest { assertTrue("All" in types) } + @Test + fun `All type resolves to persisted CelesTrak active ids`() = runTest { + // Regression: "All" is a real TLE source type (CelesTrak active). Its + // ids are persisted on sync like any other type; selecting it must + // show the synced satellites, NOT an empty list. + val fake = FakeSettingsRepoForSearch() + fake.setSatelliteTypeIds("All", sampleItems.map { it.catnum }) + val repo = SelectionRepo( + dispatcher = Dispatchers.Unconfined, + localSource = FakeLocalSourceForSearch(sampleItems), + settingsRepo = fake + ) + repo.setTypes(listOf("All")) + val results = repo.getEntriesFlow().first() + assertEquals(sampleItems.map { it.catnum }.toSet(), results.map { it.catnum }.toSet()) + } + + @Test + fun `getTypesList excludes only the Other placeholder`() = runTest { + val repo = createRepo(sampleItems) + val types = repo.getTypesList() + // All real TLE source types must be selectable, including "All" + // (CelesTrak active) and "ARISS" (removed by the old removeAt(0)). + assertTrue("All" in types) + assertTrue("ARISS" in types) + assertTrue("R4UAB" in types) + assertTrue("SatNOGS" in types) + assertTrue("Other" !in types) + } + + @Test + fun `all types shows all items`() = runTest { + val repo = createRepo(sampleItems) + repo.setTypes(emptyList()) // "All" = no type filtering + val results = repo.getEntriesFlow().first() + assertEquals(sampleItems.size, results.size) + assertEquals(sampleItems.map { it.catnum }.toSet(), results.map { it.catnum }.toSet()) + } + @Test fun `amsat list version bump re-resolves FM filter after data sync`() = runTest { // ISS module aliases: dirty pre-sync FM list contains both ZARYA and DESTINY. @@ -279,6 +318,7 @@ private class FakeSettingsRepoForSearch( var amSatLinear: Set = emptySet(), private var amSatActive: Set = emptySet() ) : ISettingsRepo { + private val typeIds = mutableMapOf>() override val appVersionName: String = "test" override val selectedIds: StateFlow> = MutableStateFlow(emptyList()) override val selectedTypes: StateFlow> = MutableStateFlow(emptyList()) @@ -313,8 +353,12 @@ private class FakeSettingsRepoForSearch( override fun setStationPosition(latitude: Double, longitude: Double, altitude: Double): Boolean = true override fun setStationPosition(): Boolean = true override fun setStationPosition(locator: String): Boolean = true - override fun getSatelliteTypesIds(types: List): List = emptyList() - override fun setSatelliteTypeIds(type: String, ids: List) = Unit + override fun getSatelliteTypesIds(types: List): List = + types.flatMap { typeIds[it].orEmpty() }.distinct() + + override fun setSatelliteTypeIds(type: String, ids: List) { + typeIds[type] = ids + } override fun updateDatabaseState(state: DatabaseState) = Unit override fun updateRCSettings(settings: RCSettings) = Unit override fun updateOtherSettings(transform: (OtherSettings) -> OtherSettings) = Unit