mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-05 19:28:25 +00:00
feat(commons): SigningOpState.Progress for per-step in-flight UI
Adds a Progress(current, total, label?) variant to SigningOpState so multi-step signing operations (NIP-17 group sends via remote signer, batched zaps) can show "Encrypting via remote signer (3 of 5)" rather than an opaque indeterminate spinner. Backwards compatible: - Pending stays a data object — existing callers' `is Pending` checks unaffected. - New helper `isPending()` returns true for both Pending and Progress; SigningState.execute uses it so a second execute() during Progress returns null (matching the old single-flight semantics). - SigningAwareButton renders both Pending and Progress as a spinner; callers wanting the counter must read the state directly. - SigningStatusBar adds a Progress branch that shows "<label> (<current> of <total>)" — uses "Signing" as default label. New `SigningState.updateProgress(current, total, label?)` lets the in-flight block emit progress updates between Pending start and finish. No-op when state is Idle/Error so callers don't have to gate. Wire-up for NIP-17 bunker sends (publishing per-recipient progress during NIP17Factory.createWraps' mapNotNullAsync) is a follow-up that depends on threading the SigningState reference into the factory's signing lambda; the substrate is here.
This commit is contained in:
+1
-1
@@ -43,7 +43,7 @@ fun SigningAwareButton(
|
||||
tint: Color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
) {
|
||||
when (signingState.state) {
|
||||
is SigningOpState.Pending -> {
|
||||
is SigningOpState.Pending, is SigningOpState.Progress -> {
|
||||
Box(modifier = modifier.size(32.dp), contentAlignment = Alignment.Center) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(16.dp),
|
||||
|
||||
+36
-1
@@ -37,11 +37,29 @@ sealed class SigningOpState {
|
||||
|
||||
data object Pending : SigningOpState()
|
||||
|
||||
/**
|
||||
* Signing is in flight AND has a known step count — typically a NIP-17
|
||||
* group send via a remote signer (bunker), where the UI can usefully show
|
||||
* "Encrypting via remote signer ([current] of [total])".
|
||||
*
|
||||
* Treated as Pending for all is-pending checks via [isPending] below;
|
||||
* existing callers that branch on `is Pending` keep working unchanged.
|
||||
* New callers can render the counter when [SigningOpState] is `Progress`.
|
||||
*/
|
||||
data class Progress(
|
||||
val current: Int,
|
||||
val total: Int,
|
||||
val label: String? = null,
|
||||
) : SigningOpState()
|
||||
|
||||
data class Error(
|
||||
val message: String,
|
||||
) : SigningOpState()
|
||||
}
|
||||
|
||||
/** True when signing is in flight, regardless of whether step counts are known. */
|
||||
fun SigningOpState.isPending(): Boolean = this is SigningOpState.Pending || this is SigningOpState.Progress
|
||||
|
||||
/**
|
||||
* Global signing status — any [SigningState] instance updates this when signing starts/ends.
|
||||
* Observe [globalState] from a screen-level composable to show a persistent status bar.
|
||||
@@ -86,7 +104,7 @@ class SigningState {
|
||||
private set
|
||||
|
||||
suspend fun <T> execute(block: suspend () -> T): T? {
|
||||
if (state is SigningOpState.Pending) return null
|
||||
if (state.isPending()) return null
|
||||
state = SigningOpState.Pending
|
||||
GlobalSigningStatus.onPending()
|
||||
errorMessage = null
|
||||
@@ -114,6 +132,23 @@ class SigningState {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the in-flight signing state with a progress counter. Use during
|
||||
* multi-step operations (NIP-17 group send via bunker, batch zaps) to
|
||||
* show the user how far the in-flight op has progressed.
|
||||
*
|
||||
* Only takes effect while [state] is Pending or Progress — no-op
|
||||
* otherwise so callers don't have to gate on Idle/Error themselves.
|
||||
*/
|
||||
fun updateProgress(
|
||||
current: Int,
|
||||
total: Int,
|
||||
label: String? = null,
|
||||
) {
|
||||
if (!state.isPending()) return
|
||||
state = SigningOpState.Progress(current, total, label)
|
||||
}
|
||||
|
||||
private fun setError(message: String) {
|
||||
errorMessage = message
|
||||
state = SigningOpState.Error(message)
|
||||
|
||||
+15
@@ -87,6 +87,21 @@ fun SigningStatusBar(
|
||||
}
|
||||
}
|
||||
|
||||
is SigningOpState.Progress -> {
|
||||
Snackbar(
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
containerColor = MaterialTheme.colorScheme.inverseSurface,
|
||||
contentColor = MaterialTheme.colorScheme.inverseOnSurface,
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
) {
|
||||
val label = opState.label ?: "Signing"
|
||||
Text(
|
||||
text = "$label (${opState.current} of ${opState.total})",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is SigningOpState.Error -> {
|
||||
Snackbar(
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
|
||||
Reference in New Issue
Block a user