Fixes all or nothing decryption of Zaps failing in a few cases. Now it has a fallback when decryption fails.
This commit is contained in:
@@ -453,7 +453,9 @@ private fun WatchZapAndRenderGallery(
|
|||||||
val zapsState by baseNote.live().zaps.observeAsState()
|
val zapsState by baseNote.live().zaps.observeAsState()
|
||||||
|
|
||||||
var zapEvents by remember(zapsState) {
|
var zapEvents by remember(zapsState) {
|
||||||
mutableStateOf<ImmutableList<ZapAmountCommentNotification>>(persistentListOf())
|
mutableStateOf<ImmutableList<ZapAmountCommentNotification>>(
|
||||||
|
accountViewModel.cachedDecryptAmountMessageInGroup(baseNote)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(key1 = zapsState) {
|
LaunchedEffect(key1 = zapsState) {
|
||||||
|
|||||||
+95
-6
@@ -71,7 +71,10 @@ import kotlinx.coroutines.channels.BufferOverflow
|
|||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
|
import kotlinx.coroutines.withTimeoutOrNull
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
import kotlin.coroutines.resume
|
||||||
import kotlin.time.measureTimedValue
|
import kotlin.time.measureTimedValue
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@@ -220,29 +223,87 @@ class AccountViewModel(val account: Account, val settings: SettingsState) : View
|
|||||||
onNewState: (ImmutableList<ZapAmountCommentNotification>) -> Unit
|
onNewState: (ImmutableList<ZapAmountCommentNotification>) -> Unit
|
||||||
) {
|
) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
allOrNothingSigningOperations<CombinedZap, ZapAmountCommentNotification>(
|
val myList = zaps.toList()
|
||||||
remainingTos = zaps,
|
|
||||||
|
val initialResults = myList.associate {
|
||||||
|
it.request to ZapAmountCommentNotification(
|
||||||
|
it.request.author,
|
||||||
|
it.request.event?.content()?.ifBlank { null },
|
||||||
|
showAmountAxis((it.response?.event as? LnZapEvent)?.amount)
|
||||||
|
)
|
||||||
|
}.toMutableMap()
|
||||||
|
|
||||||
|
collectSuccessfulSigningOperations<CombinedZap, ZapAmountCommentNotification>(
|
||||||
|
operationsInput = myList,
|
||||||
runRequestFor = { next, onReady ->
|
runRequestFor = { next, onReady ->
|
||||||
innerDecryptAmountMessage(next.request, next.response, onReady)
|
innerDecryptAmountMessage(next.request, next.response, onReady)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
onNewState(it.toImmutableList())
|
it.forEach { decrypted ->
|
||||||
|
initialResults[decrypted.key.request] = decrypted.value
|
||||||
|
}
|
||||||
|
|
||||||
|
onNewState(initialResults.values.toImmutableList())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun cachedDecryptAmountMessageInGroup(baseNote: Note): ImmutableList<ZapAmountCommentNotification> {
|
||||||
|
val myList = baseNote.zaps.toList()
|
||||||
|
|
||||||
|
return myList.map {
|
||||||
|
val request = it.first.event as? LnZapRequestEvent
|
||||||
|
if (request?.isPrivateZap() == true) {
|
||||||
|
val cachedPrivateRequest = request.cachedPrivateZap()
|
||||||
|
if (cachedPrivateRequest != null) {
|
||||||
|
ZapAmountCommentNotification(
|
||||||
|
LocalCache.getUserIfExists(cachedPrivateRequest.pubKey) ?: it.first.author,
|
||||||
|
cachedPrivateRequest.content.ifBlank { null },
|
||||||
|
showAmountAxis((it.second?.event as? LnZapEvent)?.amount)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
ZapAmountCommentNotification(
|
||||||
|
it.first.author,
|
||||||
|
it.first.event?.content()?.ifBlank { null },
|
||||||
|
showAmountAxis((it.second?.event as? LnZapEvent)?.amount)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ZapAmountCommentNotification(
|
||||||
|
it.first.author,
|
||||||
|
it.first.event?.content()?.ifBlank { null },
|
||||||
|
showAmountAxis((it.second?.event as? LnZapEvent)?.amount)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}.toImmutableList()
|
||||||
|
}
|
||||||
|
|
||||||
fun decryptAmountMessageInGroup(
|
fun decryptAmountMessageInGroup(
|
||||||
baseNote: Note,
|
baseNote: Note,
|
||||||
onNewState: (ImmutableList<ZapAmountCommentNotification>) -> Unit
|
onNewState: (ImmutableList<ZapAmountCommentNotification>) -> Unit
|
||||||
) {
|
) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
allOrNothingSigningOperations<Pair<Note, Note?>, ZapAmountCommentNotification>(
|
val myList = baseNote.zaps.toList()
|
||||||
remainingTos = baseNote.zaps.toList(),
|
|
||||||
|
val initialResults = myList.associate {
|
||||||
|
it.first to ZapAmountCommentNotification(
|
||||||
|
it.first.author,
|
||||||
|
it.first.event?.content()?.ifBlank { null },
|
||||||
|
showAmountAxis((it.second?.event as? LnZapEvent)?.amount)
|
||||||
|
)
|
||||||
|
}.toMutableMap()
|
||||||
|
|
||||||
|
collectSuccessfulSigningOperations<Pair<Note, Note?>, ZapAmountCommentNotification>(
|
||||||
|
operationsInput = myList,
|
||||||
runRequestFor = { next, onReady ->
|
runRequestFor = { next, onReady ->
|
||||||
innerDecryptAmountMessage(next.first, next.second, onReady)
|
innerDecryptAmountMessage(next.first, next.second, onReady)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
onNewState(it.toImmutableList())
|
it.forEach { decrypted ->
|
||||||
|
initialResults[decrypted.key.first] = decrypted.value
|
||||||
|
}
|
||||||
|
|
||||||
|
onNewState(initialResults.values.toImmutableList())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -999,3 +1060,31 @@ public fun <T, K> allOrNothingSigningOperations(
|
|||||||
allOrNothingSigningOperations(remainingTos.minus(next), runRequestFor, output, onReady)
|
allOrNothingSigningOperations(remainingTos.minus(next), runRequestFor, output, onReady)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public suspend fun <T, K> collectSuccessfulSigningOperations(
|
||||||
|
operationsInput: List<T>,
|
||||||
|
runRequestFor: (T, (K) -> Unit) -> Unit,
|
||||||
|
output: MutableMap<T, K> = mutableMapOf(),
|
||||||
|
onReady: (MutableMap<T, K>) -> Unit
|
||||||
|
) {
|
||||||
|
if (operationsInput.isEmpty()) {
|
||||||
|
onReady(output)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for (input in operationsInput) {
|
||||||
|
// runs in sequence to avoid overcrowding Amber.
|
||||||
|
val result = withTimeoutOrNull(100) {
|
||||||
|
suspendCancellableCoroutine { continuation ->
|
||||||
|
runRequestFor(input) { result: K ->
|
||||||
|
continuation.resume(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result != null) {
|
||||||
|
output[input] = result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onReady(output)
|
||||||
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class LnZapRequestEvent(
|
|||||||
) : Event(id, pubKey, createdAt, kind, tags, content, sig) {
|
) : Event(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||||
|
|
||||||
@Transient
|
@Transient
|
||||||
private var privateZapEvent: Event? = null
|
private var privateZapEvent: LnZapPrivateEvent? = null
|
||||||
|
|
||||||
fun zappedPost() = tags.filter { it.size > 1 && it[0] == "e" }.map { it[1] }
|
fun zappedPost() = tags.filter { it.size > 1 && it[0] == "e" }.map { it[1] }
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ class LnZapRequestEvent(
|
|||||||
|
|
||||||
fun isPrivateZap() = tags.any { t -> t.size >= 2 && t[0] == "anon" && t[1].isNotBlank() }
|
fun isPrivateZap() = tags.any { t -> t.size >= 2 && t[0] == "anon" && t[1].isNotBlank() }
|
||||||
|
|
||||||
fun getPrivateZapEvent(loggedInUserPrivKey: ByteArray, pubKey: HexKey): Event? {
|
fun getPrivateZapEvent(loggedInUserPrivKey: ByteArray, pubKey: HexKey): LnZapPrivateEvent? {
|
||||||
val anonTag = tags.firstOrNull { t -> t.size >= 2 && t[0] == "anon" }
|
val anonTag = tags.firstOrNull { t -> t.size >= 2 && t[0] == "anon" }
|
||||||
if (anonTag != null) {
|
if (anonTag != null) {
|
||||||
val encnote = anonTag[1]
|
val encnote = anonTag[1]
|
||||||
@@ -45,7 +45,7 @@ class LnZapRequestEvent(
|
|||||||
val note = decryptPrivateZapMessage(encnote, loggedInUserPrivKey, pubKey.hexToByteArray())
|
val note = decryptPrivateZapMessage(encnote, loggedInUserPrivKey, pubKey.hexToByteArray())
|
||||||
val decryptedEvent = fromJson(note)
|
val decryptedEvent = fromJson(note)
|
||||||
if (decryptedEvent.kind == 9733) {
|
if (decryptedEvent.kind == 9733) {
|
||||||
return decryptedEvent
|
return decryptedEvent as LnZapPrivateEvent
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
@@ -55,7 +55,7 @@ class LnZapRequestEvent(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cachedPrivateZap(): Event? {
|
fun cachedPrivateZap(): LnZapPrivateEvent? {
|
||||||
return privateZapEvent
|
return privateZapEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.vitorpamplona.quartz.signers
|
|||||||
import com.vitorpamplona.quartz.encoders.HexKey
|
import com.vitorpamplona.quartz.encoders.HexKey
|
||||||
import com.vitorpamplona.quartz.events.Event
|
import com.vitorpamplona.quartz.events.Event
|
||||||
import com.vitorpamplona.quartz.events.EventFactory
|
import com.vitorpamplona.quartz.events.EventFactory
|
||||||
|
import com.vitorpamplona.quartz.events.LnZapPrivateEvent
|
||||||
import com.vitorpamplona.quartz.events.LnZapRequestEvent
|
import com.vitorpamplona.quartz.events.LnZapRequestEvent
|
||||||
import com.vitorpamplona.quartz.events.PeopleListEvent
|
import com.vitorpamplona.quartz.events.PeopleListEvent
|
||||||
import kotlinx.collections.immutable.ImmutableSet
|
import kotlinx.collections.immutable.ImmutableSet
|
||||||
@@ -19,5 +20,5 @@ abstract class NostrSigner(val pubKey: HexKey) {
|
|||||||
abstract fun nip44Encrypt(decryptedContent: String, toPublicKey: HexKey, onReady: (String)-> Unit)
|
abstract fun nip44Encrypt(decryptedContent: String, toPublicKey: HexKey, onReady: (String)-> Unit)
|
||||||
abstract fun nip44Decrypt(encryptedContent: String, fromPublicKey: HexKey, onReady: (String)-> Unit)
|
abstract fun nip44Decrypt(encryptedContent: String, fromPublicKey: HexKey, onReady: (String)-> Unit)
|
||||||
|
|
||||||
abstract fun decryptZapEvent(event: LnZapRequestEvent, onReady: (Event)-> Unit)
|
abstract fun decryptZapEvent(event: LnZapRequestEvent, onReady: (LnZapPrivateEvent)-> Unit)
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,7 @@ import com.vitorpamplona.quartz.encoders.toHexKey
|
|||||||
import com.vitorpamplona.quartz.encoders.toNpub
|
import com.vitorpamplona.quartz.encoders.toNpub
|
||||||
import com.vitorpamplona.quartz.events.Event
|
import com.vitorpamplona.quartz.events.Event
|
||||||
import com.vitorpamplona.quartz.events.EventFactory
|
import com.vitorpamplona.quartz.events.EventFactory
|
||||||
|
import com.vitorpamplona.quartz.events.LnZapPrivateEvent
|
||||||
import com.vitorpamplona.quartz.events.LnZapRequestEvent
|
import com.vitorpamplona.quartz.events.LnZapRequestEvent
|
||||||
|
|
||||||
class NostrSignerExternal(
|
class NostrSignerExternal(
|
||||||
@@ -108,7 +109,7 @@ class NostrSignerExternal(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun decryptZapEvent(event: LnZapRequestEvent, onReady: (Event)-> Unit) {
|
override fun decryptZapEvent(event: LnZapRequestEvent, onReady: (LnZapPrivateEvent)-> Unit) {
|
||||||
return launcher.decryptZapEvent(event) {
|
return launcher.decryptZapEvent(event) {
|
||||||
val event = try {
|
val event = try {
|
||||||
Event.fromJson(it)
|
Event.fromJson(it)
|
||||||
@@ -116,7 +117,7 @@ class NostrSignerExternal(
|
|||||||
Log.e("NostrExternalSigner", "Unable to parse returned decrypted Zap: ${it}")
|
Log.e("NostrExternalSigner", "Unable to parse returned decrypted Zap: ${it}")
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
event?.let {
|
(event as? LnZapPrivateEvent)?.let {
|
||||||
onReady(event)
|
onReady(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ class NostrSignerInternal(val keyPair: KeyPair): NostrSigner(keyPair.pubKey.toHe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun decryptZapEvent(event: LnZapRequestEvent, onReady: (Event)-> Unit) {
|
override fun decryptZapEvent(event: LnZapRequestEvent, onReady: (LnZapPrivateEvent)-> Unit) {
|
||||||
if (keyPair.privKey == null) return
|
if (keyPair.privKey == null) return
|
||||||
|
|
||||||
val recipientPK = event.zappedAuthor().firstOrNull()
|
val recipientPK = event.zappedAuthor().firstOrNull()
|
||||||
|
|||||||
Reference in New Issue
Block a user