mirror of
https://github.com/rt-bishop/Look4Sat.git
synced 2026-10-02 03:15:37 +00:00
Tweaked passes list to show DeepSpace ones at the top
This commit is contained in:
1 parent
dde22faf46
commit
8cc4ef5610
2 files changed
+30
-59
No files matched your search
@@ -29,11 +29,8 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.grid.GridCells
|
import androidx.compose.foundation.lazy.grid.GridCells
|
||||||
import androidx.compose.foundation.lazy.grid.GridItemSpan
|
|
||||||
import androidx.compose.foundation.lazy.grid.LazyGridState
|
|
||||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||||
import androidx.compose.foundation.lazy.grid.items
|
import androidx.compose.foundation.lazy.grid.items
|
||||||
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
|
|
||||||
import androidx.compose.material3.ElevatedCard
|
import androidx.compose.material3.ElevatedCard
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material3.HorizontalDivider
|
||||||
@@ -45,8 +42,6 @@ import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
|||||||
import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults
|
import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults
|
||||||
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
|
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.derivedStateOf
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -125,7 +120,6 @@ private fun PassesScreen(
|
|||||||
onAction(PassesAction.DismissWhatsNew)
|
onAction(PassesAction.DismissWhatsNew)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val gridState = rememberLazyGridState()
|
|
||||||
ScreenColumn(
|
ScreenColumn(
|
||||||
topBar = { isVerticalLayout ->
|
topBar = { isVerticalLayout ->
|
||||||
TopBar(
|
TopBar(
|
||||||
@@ -151,7 +145,6 @@ private fun PassesScreen(
|
|||||||
passes = uiState.itemsList,
|
passes = uiState.itemsList,
|
||||||
navigateToRadar = navigateToRadar,
|
navigateToRadar = navigateToRadar,
|
||||||
refreshPasses = { onAction(PassesAction.RefreshPasses) },
|
refreshPasses = { onAction(PassesAction.RefreshPasses) },
|
||||||
gridState = gridState,
|
|
||||||
sunTimes = uiState.sunTimes
|
sunTimes = uiState.sunTimes
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -165,7 +158,6 @@ private fun PassesList(
|
|||||||
passes: List<OrbitalPass>,
|
passes: List<OrbitalPass>,
|
||||||
navigateToRadar: (Int, Long) -> Unit,
|
navigateToRadar: (Int, Long) -> Unit,
|
||||||
refreshPasses: () -> Unit,
|
refreshPasses: () -> Unit,
|
||||||
gridState: LazyGridState,
|
|
||||||
sunTimes: Map<String, Pair<String, String>>
|
sunTimes: Map<String, Pair<String, String>>
|
||||||
) {
|
) {
|
||||||
val isVerticalLayout = isVerticalLayout()
|
val isVerticalLayout = isVerticalLayout()
|
||||||
@@ -175,31 +167,15 @@ private fun PassesList(
|
|||||||
SimpleDateFormat("EEE, dd MMM yyyy", Locale.ENGLISH).also { it.timeZone = timeZone }
|
SimpleDateFormat("EEE, dd MMM yyyy", Locale.ENGLISH).also { it.timeZone = timeZone }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Group passes by calendar day so we know where headers go
|
// Deep Space first, then date-grouped timed passes
|
||||||
val groupedPasses = remember(passes, isUtc) {
|
val groupedPasses = remember(passes, isUtc) {
|
||||||
passes.filter { !it.isDeepSpace }.groupBy { sdfDate.format(Date(it.aosTime)) } +
|
val ordered = LinkedHashMap<String, List<OrbitalPass>>()
|
||||||
if (passes.any { it.isDeepSpace }) mapOf("Deep Space" to passes.filter { it.isDeepSpace }) else emptyMap()
|
val deepSpace = passes.filter { it.isDeepSpace }
|
||||||
}
|
if (deepSpace.isNotEmpty()) ordered["Deep Space"] = deepSpace
|
||||||
|
passes.filter { !it.isDeepSpace }
|
||||||
// Derive the sticky header label from the first visible item index
|
.groupByTo(LinkedHashMap()) { sdfDate.format(Date(it.aosTime)) }
|
||||||
val stickyHeader by remember(gridState, groupedPasses) {
|
.forEach { (k, v) -> ordered[k] = v }
|
||||||
derivedStateOf {
|
ordered
|
||||||
val firstVisible = gridState.firstVisibleItemIndex
|
|
||||||
var itemIndex = 0
|
|
||||||
var header = groupedPasses.keys.firstOrNull() ?: ""
|
|
||||||
for ((key, items) in groupedPasses) {
|
|
||||||
header = key
|
|
||||||
itemIndex += 1 + items.size
|
|
||||||
if (itemIndex > firstVisible) break
|
|
||||||
}
|
|
||||||
header
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The sticky overlay should only show when the first in-list header has scrolled out of view.
|
|
||||||
// firstVisibleItemIndex == 0 means the first header is still visible — hide the overlay.
|
|
||||||
val showStickyOverlay by remember(gridState) {
|
|
||||||
derivedStateOf { gridState.firstVisibleItemIndex > 0 || gridState.firstVisibleItemScrollOffset > 0 }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ElevatedCard(modifier = Modifier.fillMaxSize()) {
|
ElevatedCard(modifier = Modifier.fillMaxSize()) {
|
||||||
@@ -220,32 +196,20 @@ private fun PassesList(
|
|||||||
if (passes.isEmpty()) {
|
if (passes.isEmpty()) {
|
||||||
EmptyListCard(message = stringResource(R.string.pass_empty_list_message))
|
EmptyListCard(message = stringResource(R.string.pass_empty_list_message))
|
||||||
} else {
|
} else {
|
||||||
Column {
|
LazyVerticalGrid(columns = GridCells.Adaptive(320.dp), modifier = Modifier.fillMaxSize()) {
|
||||||
// Sticky header overlay — only visible once the in-list header scrolls away
|
for ((dateLabel, dayPasses) in groupedPasses) {
|
||||||
if (showStickyOverlay) {
|
stickyHeader(key = "header_$dateLabel") {
|
||||||
val (rise, set) = sunTimes[stickyHeader] ?: ("--:--" to "--:--")
|
val (rise, set) = sunTimes[dateLabel] ?: ("--:--" to "--:--")
|
||||||
StickyDateHeader(label = stickyHeader, sunriseTime = rise, sunsetTime = set)
|
StickyDateHeader(label = dateLabel, sunriseTime = rise, sunsetTime = set)
|
||||||
}
|
}
|
||||||
LazyVerticalGrid(
|
items(items = dayPasses, key = { item -> item.catNum + item.aosTime }) { pass ->
|
||||||
state = gridState,
|
PassItem(
|
||||||
columns = GridCells.Adaptive(320.dp),
|
pass = pass,
|
||||||
modifier = Modifier.fillMaxSize()
|
navigateToRadar = navigateToRadar,
|
||||||
) {
|
modifier = Modifier.animateItem(),
|
||||||
for ((dateLabel, dayPasses) in groupedPasses) {
|
isVerticalLayout = isVerticalLayout,
|
||||||
// In-list section header (scrolls away, drives the sticky overlay)
|
isUtc = isUtc
|
||||||
item(span = { GridItemSpan(maxLineSpan) }) {
|
)
|
||||||
val (rise, set) = sunTimes[dateLabel] ?: ("--:--" to "--:--")
|
|
||||||
StickyDateHeader(label = dateLabel, sunriseTime = rise, sunsetTime = set)
|
|
||||||
}
|
|
||||||
items(items = dayPasses, key = { item -> item.catNum + item.aosTime }) { pass ->
|
|
||||||
PassItem(
|
|
||||||
pass = pass,
|
|
||||||
navigateToRadar = navigateToRadar,
|
|
||||||
modifier = Modifier.animateItem(),
|
|
||||||
isVerticalLayout = isVerticalLayout,
|
|
||||||
isUtc = isUtc
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-1
@@ -124,13 +124,20 @@ class PassesViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Computes sunrise/sunset strings for each unique calendar day in the pass list. */
|
// Computes sunrise/sunset strings for each unique calendar day in the pass list, plus today for Deep Space
|
||||||
private fun computeSunTimes(passes: List<OrbitalPass>, isUtc: Boolean): Map<String, Pair<String, String>> {
|
private fun computeSunTimes(passes: List<OrbitalPass>, isUtc: Boolean): Map<String, Pair<String, String>> {
|
||||||
val stationPos = settingsRepo.stationPosition.value
|
val stationPos = settingsRepo.stationPosition.value
|
||||||
val tz = if (isUtc) TimeZone.getTimeZone("UTC") else TimeZone.getDefault()
|
val tz = if (isUtc) TimeZone.getTimeZone("UTC") else TimeZone.getDefault()
|
||||||
val sdfDate = SimpleDateFormat("EEE, dd MMM yyyy", Locale.ENGLISH).also { it.timeZone = tz }
|
val sdfDate = SimpleDateFormat("EEE, dd MMM yyyy", Locale.ENGLISH).also { it.timeZone = tz }
|
||||||
val sdfTime = SimpleDateFormat("HH:mm", Locale.ENGLISH).also { it.timeZone = tz }
|
val sdfTime = SimpleDateFormat("HH:mm", Locale.ENGLISH).also { it.timeZone = tz }
|
||||||
val result = LinkedHashMap<String, Pair<String, String>>()
|
val result = LinkedHashMap<String, Pair<String, String>>()
|
||||||
|
// Deep Space group always shows today's sun times
|
||||||
|
if (passes.any { it.isDeepSpace }) {
|
||||||
|
val riseSet = CelestialComputer.findSunRiseSet(stationPos, System.currentTimeMillis())
|
||||||
|
val rise = if (riseSet.riseTimeMillis > 0) sdfTime.format(Date(riseSet.riseTimeMillis)) else "--:--"
|
||||||
|
val set = if (riseSet.setTimeMillis > 0) sdfTime.format(Date(riseSet.setTimeMillis)) else "--:--"
|
||||||
|
result["Deep Space"] = rise to set
|
||||||
|
}
|
||||||
for (pass in passes) {
|
for (pass in passes) {
|
||||||
if (pass.isDeepSpace) continue
|
if (pass.isDeepSpace) continue
|
||||||
val label = sdfDate.format(Date(pass.aosTime))
|
val label = sdfDate.format(Date(pass.aosTime))
|
||||||
|
|||||||
Reference in new issue
Block a user