neiam
/apollos_crib
It's Apollo's Crib
public · Issues · Pulls 1 · Labels · Forks · Compare · Actions failure · Badges · Packages
⭐
Log in to mark this repository.
Give the single card a turn of its own
48d3a04 · Gabriel Morell · 2026-09-08 20:46
Message
{commit_body(@commit)}
Files changed
modified
clients/android/app/src/main/java/io/neiam/apolloscrib/ui/BoardScreen.kt
+42
−0
@@ -1,5 +1,11 @@
| 1 | 1 | package io.neiam.apolloscrib.ui |
| 2 | 2 | |
| 3 | +import androidx.compose.animation.AnimatedContent | |
| 4 | +import androidx.compose.animation.fadeIn | |
| 5 | +import androidx.compose.animation.fadeOut | |
| 6 | +import androidx.compose.animation.slideInVertically | |
| 7 | +import androidx.compose.animation.slideOutVertically | |
| 8 | +import androidx.compose.animation.togetherWith | |
| 3 | 9 | import androidx.compose.animation.core.Spring |
| 4 | 10 | import androidx.compose.animation.core.spring |
| 5 | 11 | import androidx.compose.animation.core.tween |
@@ -283,6 +289,41 @@ fun BoardScreen(
| 283 | 289 | } |
| 284 | 290 | } |
| 285 | 291 | |
| 292 | + // One card is a different animation from three. | |
| 293 | + // | |
| 294 | + // With a stack, a turn moves two cards and replaces one, and the | |
| 295 | + // movement is the story -- `animateItem` shows what stayed. With a | |
| 296 | + // single card nothing stays, so there is nothing to move and a | |
| 297 | + // fade is all a list animation can offer. That reads as a stutter | |
| 298 | + // rather than a turn. | |
| 299 | + // | |
| 300 | + // So it gets its own: the old card leaves upward and the new one | |
| 301 | + // arrives from below, which is the direction a flick pushes it. | |
| 302 | + // The gesture and the animation then agree about which way the | |
| 303 | + // board runs, and the timer's turn looks like the one you make. | |
| 304 | + if (richCards == 1 && turn.window.isNotEmpty()) { | |
| 305 | + item(key = "single") { | |
| 306 | + val card = cards[turn.window.first()] | |
| 307 | + | |
| 308 | + AnimatedContent( | |
| 309 | + targetState = card, | |
| 310 | + transitionSpec = { | |
| 311 | + (slideInVertically(tween(320)) { height -> height } + | |
| 312 | + fadeIn(tween(220))) | |
| 313 | + .togetherWith( | |
| 314 | + slideOutVertically(tween(320)) { height -> -height } + | |
| 315 | + fadeOut(tween(180)) | |
| 316 | + ) | |
| 317 | + }, | |
| 318 | + label = "card" | |
| 319 | + ) { shown -> | |
| 320 | + when (shown) { | |
| 321 | + is RichItem.Transit -> RichCard(shown.entry, shown.route) | |
| 322 | + is RichItem.Facts -> RichFactsCard(shown.facts) | |
| 323 | + } | |
| 324 | + } | |
| 325 | + } | |
| 326 | + } else { | |
| 286 | 327 | items(turn.window.map { cards[it] }, key = { it.key }) { card -> |
| 287 | 328 | // Keyed, so the list knows the two cards that stayed are the |
| 288 | 329 | // same two cards. Without that every turn is three removals |
@@ -299,6 +340,7 @@ fun BoardScreen(
| 299 | 340 | is RichItem.Facts -> RichFactsCard(card.facts, slide) |
| 300 | 341 | } |
| 301 | 342 | } |
| 343 | + } | |
| 302 | 344 | } else { |
| 303 | 345 | entries.forEach { entry -> |
| 304 | 346 | // No preview means the renderer had nothing worth a card -- a |
Parents: b4bebfd