feat(satellites): 勾选置顶显示——已选/可用分组+顶部计数条(对齐上游)

This commit is contained in:
atsunatsu committed 2026-10-08 02:26:56 +08:00
1 parent bb001b4d38
commit 74e7e19cf2
10 files changed
+101 -3

No files matched your search

@@ -66,8 +66,17 @@ class SelectionRepo(
}
}
// Selected satellites are pinned to the top (matches upstream); ordering
// within each group stays name/catnum. Sits on the reactive chain, so
// ticking an entry re-sorts the visible list immediately.
private val itemsWithQuery = currentQuery.flatMapLatest { query ->
itemsWithTypes.map { items -> filterByQuery(items, query) }
itemsWithTypes.map { items ->
filterByQuery(items, query).sortedWith(
compareByDescending<SatItem> { it.isSelected }
.thenBy { it.name }
.thenBy { it.catnum }
)
}
}
override fun getCurrentTypes() = currentTypes.value
@@ -284,6 +284,24 @@ class SelectionRepoSearchTest {
assertEquals(sampleItems.map { it.catnum }.toSet(), results.map { it.catnum }.toSet())
}
@Test
fun `selected satellites are shown first`() = runTest {
// Pinning ported from upstream: ticked satellites sort above the rest,
// each group keeping the name order.
val repo = createRepo(
items = listOf(
SatItem(catnum = 44444, name = "Zeta"),
SatItem(catnum = 25544, name = "Alpha"),
SatItem(catnum = 40967, name = "Beta")
)
)
val flow = repo.getEntriesFlow()
repo.setSelection(listOf(40967), true)
val results = flow.first()
assertEquals(listOf(40967, 25544, 44444), results.map { it.catnum })
assertEquals(listOf(true, false, false), results.map { it.isSelected })
}
private fun createRepo(
items: List<SatItem>,
sstvIds: List<Int> = emptyList()
@@ -19,6 +19,10 @@
<string name="sat_search_clear">Vaciar</string>
<string name="sat_clear_all">Vaciar todo</string>
<string name="sat_select_all">Seleccionar todo</string>
<string name="sat_group_selected">Seleccionados</string>
<string name="sat_group_available">Disponibles</string>
<string name="sat_group_selected_count">Seleccionados (%1$d)</string>
<string name="sat_group_available_count">Disponibles (%1$d)</string>
<string name="sat_empty_list_message">
Asegúrese de que su consulta de búsqueda sea correcta y que la base de datos esté actualizada</string>
<string name="sat_warning_title">Alarma\!</string>
@@ -19,6 +19,10 @@
<string name="sat_search_clear">Очистить</string>
<string name="sat_clear_all">Очистить</string>
<string name="sat_select_all">Выбрать</string>
<string name="sat_group_selected">Выбранные</string>
<string name="sat_group_available">Доступные</string>
<string name="sat_group_selected_count">Выбранные (%1$d)</string>
<string name="sat_group_available_count">Доступные (%1$d)</string>
<string name="sat_empty_list_message">Убедитесь, что ваш поисковый запрос верен и база данных обновлена</string>
<string name="sat_warning_title">Внимание\!</string>
<string name="sat_warning_message">
@@ -19,6 +19,10 @@
<string name="sat_search_clear">පිරිසිදු ක°</string>
<string name="sat_clear_all">සියල්ල පිරිසිදු ක°</string>
<string name="sat_select_all">සියල්ල තෝරන්න</string>
<string name="sat_group_selected">තෝරාගත්</string>
<string name="sat_group_available">තිබෙන</string>
<string name="sat_group_selected_count">තෝරාගත් (%1$d)</string>
<string name="sat_group_available_count">තිබෙන (%1$d)</string>
<string name="sat_empty_list_message">
ඔබගේ සෙවුම් විමසුම නිවැරදි බවත් දත්ත සමුදාය යාවත්කාලීන කර ඇති බවත් සහතික කර ගන්න</string>
<string name="sat_warning_title">අවවාදයයි\!</string>
@@ -20,6 +20,10 @@
<string name="sat_search_clear">Temizle</string>
<string name="sat_clear_all">Tümünü temizle</string>
<string name="sat_select_all">Tümünü seç</string>
<string name="sat_group_selected">Seçilenler</string>
<string name="sat_group_available">Kullanılabilir</string>
<string name="sat_group_selected_count">Seçilenler (%1$d)</string>
<string name="sat_group_available_count">Kullanılabilir (%1$d)</string>
<string name="sat_empty_list_message">"Arama sorgunuzun doğru olduğundan ve veritabanının güncellendiğinden emin olun"</string>
<string name="sat_warning_title">Uyarı\!</string>
<string name="sat_warning_message">
@@ -19,6 +19,10 @@
<string name="sat_search_clear">Очистити</string>
<string name="sat_clear_all">Очистити всі</string>
<string name="sat_select_all">Вибрати всі</string>
<string name="sat_group_selected">Обрані</string>
<string name="sat_group_available">Доступні</string>
<string name="sat_group_selected_count">Обрані (%1$d)</string>
<string name="sat_group_available_count">Доступні (%1$d)</string>
<string name="sat_empty_list_message">
Переконайтеся, що ваш пошуковий запит правильний, а база даних оновлена</string>
<string name="sat_warning_title">Увага\!</string>
@@ -20,6 +20,10 @@
<string name="sat_search_clear">清空</string>
<string name="sat_clear_all">全部清空</string>
<string name="sat_select_all">全选</string>
<string name="sat_group_selected">已选择</string>
<string name="sat_group_available">可用</string>
<string name="sat_group_selected_count">已选择(%1$d)</string>
<string name="sat_group_available_count">可用(%1$d)</string>
<string name="sat_empty_list_message">请确保您的搜索查询正确且数据库已更新</string>
<string name="sat_warning_title">警告\!</string>
<string name="sat_warning_message">此应用收录了超过9000颗卫星信息\n同时追踪所有卫星并不现实\n建议始终通过搜索和分类筛选功能仅勾选您感兴趣的卫星</string>
@@ -21,6 +21,10 @@
<string name="sat_search_clear">Clear</string>
<string name="sat_clear_all">Clear all</string>
<string name="sat_select_all">Select all</string>
<string name="sat_group_selected">Selected</string>
<string name="sat_group_available">Available</string>
<string name="sat_group_selected_count">Selected (%1$d)</string>
<string name="sat_group_available_count">Available (%1$d)</string>
<string name="sat_empty_list_message">"Make sure your search query is correct and the DB is updated"</string>
<string name="sat_warning_title">Warning\!</string>
<string name="sat_warning_message">
@@ -326,9 +326,52 @@ private fun SatellitesPreview() {
@Composable
private fun SatellitesCard(items: List<SatItem>, onSelected: (Int, Boolean) -> Unit) {
val selectedItems = items.filter { it.isSelected }
val availableItems = items.filterNot { it.isSelected }
LazyVerticalGrid(columns = GridCells.Adaptive(320.dp)) {
items(items = items, key = { item -> item.catnum }) { entry ->
Satellite(entry, onSelected, Modifier.animateItem())
stickyHeader(key = "sat_summary_header") {
SummaryHeader(
selectedText = stringResource(R.string.sat_group_selected_count, selectedItems.size),
availableText = stringResource(R.string.sat_group_available_count, availableItems.size)
)
}
if (selectedItems.isNotEmpty()) {
items(items = selectedItems, key = { item -> item.catnum }) { entry ->
Satellite(entry, onSelected, Modifier.animateItem())
}
}
if (availableItems.isNotEmpty()) {
items(items = availableItems, key = { item -> item.catnum }) { entry ->
Satellite(entry, onSelected, Modifier.animateItem())
}
}
}
}
@Composable
private fun SummaryHeader(selectedText: String, availableText: String) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.surfaceContainerHighest)
.padding(horizontal = 12.dp, vertical = 4.dp)
) {
Text(
text = selectedText,
style = MaterialTheme.typography.titleSmall,
fontWeight = FontWeight.SemiBold,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Text(
text = availableText,
style = MaterialTheme.typography.titleSmall,
fontWeight = FontWeight.SemiBold,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}