diff --git a/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentComposable.kt b/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentComposable.kt
index 3663d1d3..9fb20a0e 100644
--- a/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentComposable.kt
+++ b/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentComposable.kt
@@ -58,6 +58,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableLongStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
@@ -71,6 +72,7 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
+import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import net.taler.common.Amount
import net.taler.common.ContractChoice
@@ -83,6 +85,7 @@ import net.taler.common.ContractTokenFamily
import net.taler.common.Exchange
import net.taler.common.Merchant
import net.taler.common.TalerUtils
+import net.taler.common.Timestamp
import net.taler.lib.android.base64Bitmap
import net.taler.wallet.R
import net.taler.wallet.cleanExchange
@@ -182,8 +185,12 @@ fun PromptPaymentComposable(
}
}
+ val timeToPay = rememberTimeToPay(contractTerms.payDeadline)
BottomButtonBox(Modifier.fillMaxWidth(),
- heading = if (contractTerms is ContractTerms.V0) { ->
+ heading = if (contractTerms is ContractTerms.V0 || timeToPay != null) { ->
+ timeToPay?.let { TimeToPay(it) }
+
+ if (contractTerms !is ContractTerms.V0) return@BottomButtonBox
val choice = status.choices.firstOrNull()
?: error("no v0 choice")
@@ -247,6 +254,40 @@ fun PromptPaymentComposable(
}
}
+/** Orders due within this time show a countdown, as in the iOS wallet. */
+private const val TIME_TO_PAY_WARNING_MS = 5 * 60 * 1000L
+
+/**
+ * Milliseconds left until the payment deadline once it is less than
+ * [TIME_TO_PAY_WARNING_MS] away, updated every second; null before that.
+ */
+@Composable
+private fun rememberTimeToPay(payDeadline: Timestamp?): Long? {
+ val deadlineMs = payDeadline?.ms?.takeIf { it > 0 } ?: return null
+ var now by remember { mutableLongStateOf(System.currentTimeMillis()) }
+ val remaining = deadlineMs - now
+ LaunchedEffect(now) {
+ if (remaining > 0) {
+ delay(if (remaining > TIME_TO_PAY_WARNING_MS) remaining - TIME_TO_PAY_WARNING_MS else 1000L)
+ now = System.currentTimeMillis()
+ }
+ }
+ return if (remaining > TIME_TO_PAY_WARNING_MS) null else remaining.coerceAtLeast(0)
+}
+
+@Composable
+private fun TimeToPay(remainingMs: Long) {
+ val seconds = (remainingMs + 999) / 1000
+ Text(
+ modifier = Modifier.padding(bottom = 3.dp),
+ text = stringResource(
+ R.string.payment_time_to_pay,
+ "%d:%02d".format(seconds / 60, seconds % 60),
+ ),
+ style = MaterialTheme.typography.labelLarge,
+ )
+}
+
@Composable
fun MerchantSection(
contractTerms: ContractTerms,
diff --git a/wallet/src/main/res/values/strings.xml b/wallet/src/main/res/values/strings.xml
index 89750709..77b6b67c 100644
--- a/wallet/src/main/res/values/strings.xml
+++ b/wallet/src/main/res/values/strings.xml
@@ -320,6 +320,7 @@ GNU Taler is immune to many types of fraud such as credit card data theft, phish
Unexpected merchant %1$s, your token could be stolen.
Merchant %1$s is untrusted, cannot spend this token.
Error creating order
+ Time to pay: %1$s
Payment