From 323b10224c5f8e008e5bc28e970d8f2f8ee16232 Mon Sep 17 00:00:00 2001 From: darken Date: Thu, 6 Aug 2026 09:02:57 +0200 Subject: [PATCH] fix(overview): Latch the review card's actions after a tap The card stays up until the next state emission, so a dismiss after a review would overwrite the review bookkeeping with a snooze and a review after a dismiss would re-open what was just closed. Repeated review taps stay allowed so a failed Play request can still be retried. --- .../main/ui/overview/cards/ReviewCard.kt | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/ReviewCard.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/ReviewCard.kt index 77ebac0f..b1a412f5 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/ReviewCard.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/ReviewCard.kt @@ -17,6 +17,10 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource @@ -34,6 +38,13 @@ fun ReviewCard( onReview: (() -> Unit)?, onDismiss: () -> Unit, ) { + // The card only disappears with the next state emission, so the tap targets need a latch. It is + // asymmetric on purpose: the harmful orderings are a dismiss after a review (which overwrites + // the review bookkeeping with a snooze) and a review after a dismiss. A repeated review tap is + // harmless, the tool's single-flight lock absorbs it, and blocking it here would leave a dead + // card whenever a Play request fails and nothing gets persisted. + var dismissLocked by rememberSaveable { mutableStateOf(false) } + var fullyLatched by rememberSaveable { mutableStateOf(false) } Card( modifier = Modifier .fillMaxWidth() @@ -71,13 +82,27 @@ fun ReviewCard( horizontalArrangement = Arrangement.End, verticalAlignment = Alignment.CenterVertically, ) { - TextButton(onClick = onDismiss) { + TextButton( + onClick = { + if (!fullyLatched && !dismissLocked) { + dismissLocked = true + fullyLatched = true + onDismiss() + } + }, + enabled = !fullyLatched && !dismissLocked, + ) { Text(text = stringResource(R.string.review_app_dismiss_action)) } Spacer(modifier = Modifier.width(8.dp)) Button( - onClick = { onReview?.invoke() }, - enabled = onReview != null, + onClick = { + if (!fullyLatched && onReview != null) { + dismissLocked = true + onReview.invoke() + } + }, + enabled = onReview != null && !fullyLatched, ) { Text(text = stringResource(R.string.review_app_review_action)) }