diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip46Signer/Nip46ActivityLog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip46Signer/Nip46ActivityLog.kt index 0cbc9b2a14..d047d23314 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip46Signer/Nip46ActivityLog.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip46Signer/Nip46ActivityLog.kt @@ -53,7 +53,4 @@ class Nip46ActivityLog( fun record(entry: Nip46ActivityEntry) { _entries.update { (listOf(entry) + it).take(capacity) } } - - /** The most recent entries for one client (newest first). */ - fun forClient(clientPubKey: HexKey): List = _entries.value.filter { it.clientPubKey == clientPubKey } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip46Signer/Nip46ConsentBridge.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip46Signer/Nip46ConsentBridge.kt index 19bbce1500..53e78a2e02 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip46Signer/Nip46ConsentBridge.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip46Signer/Nip46ConsentBridge.kt @@ -49,8 +49,9 @@ import kotlinx.coroutines.withTimeoutOrNull */ object Nip46ConsentBridge { /** - * Upper bound on how long a per-op prompt may block the signer's single-consumer loop. A user who - * ignores the dialog eventually fails the request closed (deny-once) instead of wedging the signer. + * Upper bound on how long a consent prompt may block the signer's single-consumer loop. A user who + * ignores the dialog eventually fails the request closed (deny / declined) instead of wedging the + * signer for every other client whose requests queue behind that one blocked prompt. */ private const val CONSENT_TIMEOUT_MS = 120_000L @@ -64,10 +65,12 @@ object Nip46ConsentBridge { val meta = request.clientMetadata val title = meta?.name?.ifBlank { null } ?: context.getString(R.string.nip46_signer_remote_app) val domain = meta?.url?.ifBlank { null } ?: (clientPubKey.take(12) + "…") - return SignerConnectCoordinator.requestConnect( - context, - SignerConnectInfo(appletTitle = title, coordinate = coordinate, domain = domain, iconUrl = meta?.image), - ) + val info = SignerConnectInfo(appletTitle = title, coordinate = coordinate, domain = domain, iconUrl = meta?.image) + // Fail closed (declined) if the prompt is never answered, so a stuck first-connect dialog can't + // hold the single-consumer loop hostage against every other client. + return withTimeoutOrNull(CONSENT_TIMEOUT_MS) { + SignerConnectCoordinator.requestConnect(context, info) + } ?: AppConnectResult.Cancelled } /** Per-operation consent: describe the request (op + event preview) and await the user's grant. */