Merge remote-tracking branch 'upstream/main'
This commit is contained in:
@@ -20,6 +20,8 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import nostr.postr.Persona
|
import nostr.postr.Persona
|
||||||
|
import nostr.postr.Utils
|
||||||
|
import java.math.BigDecimal
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
val DefaultChannels = setOf(
|
val DefaultChannels = setOf(
|
||||||
@@ -170,7 +172,32 @@ class Account(
|
|||||||
return zapPaymentRequest != null
|
return zapPaymentRequest != null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendZapPaymentRequestFor(bolt11: String, onResponse: (Response?) -> Unit) {
|
fun isNIP47Author(pubkeyHex: String?): Boolean {
|
||||||
|
val privKey = zapPaymentRequest?.secret?.toByteArray() ?: loggedIn.privKey!!
|
||||||
|
val pubKey = Utils.pubkeyCreate(privKey).toHexKey()
|
||||||
|
return (pubKey == pubkeyHex)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decryptZapPaymentResponseEvent(zapResponseEvent: LnZapPaymentResponseEvent): Response? {
|
||||||
|
val myNip47 = zapPaymentRequest ?: return null
|
||||||
|
return zapResponseEvent.response(
|
||||||
|
myNip47.secret?.toByteArray() ?: loggedIn.privKey!!,
|
||||||
|
myNip47.pubKeyHex.toByteArray()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun calculateIfNoteWasZappedByAccount(zappedNote: Note): Boolean {
|
||||||
|
return zappedNote.isZappedBy(userProfile(), this) == true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun calculateZappedAmount(zappedNote: Note?): BigDecimal {
|
||||||
|
return zappedNote?.zappedAmount(
|
||||||
|
zapPaymentRequest?.secret?.toByteArray() ?: loggedIn.privKey!!,
|
||||||
|
zapPaymentRequest?.pubKeyHex?.toByteArray()
|
||||||
|
) ?: BigDecimal.ZERO
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sendZapPaymentRequestFor(bolt11: String, zappedNote: Note?, onResponse: (Response?) -> Unit) {
|
||||||
if (!isWriteable()) return
|
if (!isWriteable()) return
|
||||||
|
|
||||||
zapPaymentRequest?.let { nip47 ->
|
zapPaymentRequest?.let { nip47 ->
|
||||||
@@ -179,11 +206,11 @@ class Account(
|
|||||||
val wcListener = NostrLnZapPaymentResponseDataSource(nip47.pubKeyHex, event.pubKey, event.id)
|
val wcListener = NostrLnZapPaymentResponseDataSource(nip47.pubKeyHex, event.pubKey, event.id)
|
||||||
wcListener.start()
|
wcListener.start()
|
||||||
|
|
||||||
LocalCache.consume(event) {
|
LocalCache.consume(event, zappedNote) {
|
||||||
// After the response is received.
|
// After the response is received.
|
||||||
val privKey = nip47.secret?.toByteArray()
|
val privKey = nip47.secret?.toByteArray()
|
||||||
if (privKey != null) {
|
if (privKey != null) {
|
||||||
onResponse(it.response(privKey, event.pubKey.toByteArray()))
|
onResponse(it.response(privKey, nip47.pubKeyHex.toByteArray()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import android.util.Log
|
|||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import com.fasterxml.jackson.databind.DeserializationFeature
|
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||||
import com.vitorpamplona.amethyst.service.NostrAccountDataSource.account
|
|
||||||
import com.vitorpamplona.amethyst.service.model.*
|
import com.vitorpamplona.amethyst.service.model.*
|
||||||
import com.vitorpamplona.amethyst.service.relays.Relay
|
import com.vitorpamplona.amethyst.service.relays.Relay
|
||||||
import com.vitorpamplona.amethyst.ui.components.BundledInsert
|
import com.vitorpamplona.amethyst.ui.components.BundledInsert
|
||||||
@@ -31,7 +30,8 @@ object LocalCache {
|
|||||||
val channels = ConcurrentHashMap<HexKey, Channel>()
|
val channels = ConcurrentHashMap<HexKey, Channel>()
|
||||||
val addressables = ConcurrentHashMap<String, AddressableNote>(100)
|
val addressables = ConcurrentHashMap<String, AddressableNote>(100)
|
||||||
|
|
||||||
val awaitingPaymentRequests = ConcurrentHashMap<HexKey, (LnZapPaymentResponseEvent) -> Unit>(10)
|
val awaitingPaymentRequests =
|
||||||
|
ConcurrentHashMap<HexKey, Pair<Note?, (LnZapPaymentResponseEvent) -> Unit>>(10)
|
||||||
|
|
||||||
fun checkGetOrCreateUser(key: String): User? {
|
fun checkGetOrCreateUser(key: String): User? {
|
||||||
if (isValidHexNpub(key)) {
|
if (isValidHexNpub(key)) {
|
||||||
@@ -379,6 +379,7 @@ object LocalCache {
|
|||||||
masterNote.removeBoost(deleteNote)
|
masterNote.removeBoost(deleteNote)
|
||||||
masterNote.removeReaction(deleteNote)
|
masterNote.removeReaction(deleteNote)
|
||||||
masterNote.removeZap(deleteNote)
|
masterNote.removeZap(deleteNote)
|
||||||
|
masterNote.removeZapPayment(deleteNote)
|
||||||
masterNote.removeReport(deleteNote)
|
masterNote.removeReport(deleteNote)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -728,12 +729,41 @@ object LocalCache {
|
|||||||
// Does nothing without a response callback.
|
// Does nothing without a response callback.
|
||||||
}
|
}
|
||||||
|
|
||||||
fun consume(event: LnZapPaymentRequestEvent, onResponse: (LnZapPaymentResponseEvent) -> Unit) {
|
fun consume(event: LnZapPaymentRequestEvent, zappedNote: Note?, onResponse: (LnZapPaymentResponseEvent) -> Unit) {
|
||||||
awaitingPaymentRequests.put(event.id, onResponse)
|
val note = getOrCreateNote(event.id)
|
||||||
|
val author = getOrCreateUser(event.pubKey)
|
||||||
|
|
||||||
|
// Already processed this event.
|
||||||
|
if (note.event != null) return
|
||||||
|
|
||||||
|
note.loadEvent(event, author, emptyList())
|
||||||
|
|
||||||
|
zappedNote?.addZapPayment(note, null)
|
||||||
|
|
||||||
|
awaitingPaymentRequests.put(event.id, Pair(zappedNote, onResponse))
|
||||||
|
|
||||||
|
refreshObservers(note)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun consume(event: LnZapPaymentResponseEvent) {
|
fun consume(event: LnZapPaymentResponseEvent) {
|
||||||
val responseCallback = awaitingPaymentRequests[event.requestId()]
|
val requestId = event.requestId()
|
||||||
|
val pair = awaitingPaymentRequests[requestId] ?: return
|
||||||
|
|
||||||
|
val (zappedNote, responseCallback) = pair
|
||||||
|
|
||||||
|
val requestNote = requestId?.let { checkGetOrCreateNote(requestId) }
|
||||||
|
|
||||||
|
val note = getOrCreateNote(event.id)
|
||||||
|
val author = getOrCreateUser(event.pubKey)
|
||||||
|
|
||||||
|
// Already processed this event.
|
||||||
|
if (note.event != null) return
|
||||||
|
|
||||||
|
note.loadEvent(event, author, emptyList())
|
||||||
|
|
||||||
|
requestNote?.let { request ->
|
||||||
|
zappedNote?.addZapPayment(request, note)
|
||||||
|
}
|
||||||
|
|
||||||
if (responseCallback != null) {
|
if (responseCallback != null) {
|
||||||
responseCallback(event)
|
responseCallback(event)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.vitorpamplona.amethyst.model
|
|||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import com.vitorpamplona.amethyst.service.NostrSingleEventDataSource
|
import com.vitorpamplona.amethyst.service.NostrSingleEventDataSource
|
||||||
|
import com.vitorpamplona.amethyst.service.lnurl.LnInvoiceUtil
|
||||||
import com.vitorpamplona.amethyst.service.model.*
|
import com.vitorpamplona.amethyst.service.model.*
|
||||||
import com.vitorpamplona.amethyst.service.nip19.Nip19
|
import com.vitorpamplona.amethyst.service.nip19.Nip19
|
||||||
import com.vitorpamplona.amethyst.service.relays.EOSETime
|
import com.vitorpamplona.amethyst.service.relays.EOSETime
|
||||||
@@ -45,6 +46,8 @@ open class Note(val idHex: String) {
|
|||||||
private set
|
private set
|
||||||
var zaps = mapOf<Note, Note?>()
|
var zaps = mapOf<Note, Note?>()
|
||||||
private set
|
private set
|
||||||
|
var zapPayments = mapOf<Note, Note?>()
|
||||||
|
private set
|
||||||
|
|
||||||
var relays = setOf<String>()
|
var relays = setOf<String>()
|
||||||
private set
|
private set
|
||||||
@@ -156,6 +159,17 @@ open class Note(val idHex: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun removeZapPayment(note: Note) {
|
||||||
|
if (zapPayments[note] != null) {
|
||||||
|
zapPayments = zapPayments.minus(note)
|
||||||
|
liveSet?.zaps?.invalidateData()
|
||||||
|
} else if (zapPayments.containsValue(note)) {
|
||||||
|
val toRemove = zapPayments.filterValues { it == note }
|
||||||
|
zapPayments = zapPayments.minus(toRemove.keys)
|
||||||
|
liveSet?.zaps?.invalidateData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun addBoost(note: Note) {
|
fun addBoost(note: Note) {
|
||||||
if (note !in boosts) {
|
if (note !in boosts) {
|
||||||
boosts = boosts + note
|
boosts = boosts + note
|
||||||
@@ -174,6 +188,17 @@ open class Note(val idHex: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun addZapPayment(zapPaymentRequest: Note, zapPayment: Note?) {
|
||||||
|
if (zapPaymentRequest !in zapPayments.keys) {
|
||||||
|
zapPayments = zapPayments + Pair(zapPaymentRequest, zapPayment)
|
||||||
|
liveSet?.zaps?.invalidateData()
|
||||||
|
} else if (zapPaymentRequest in zapPayments.keys && zapPayments[zapPaymentRequest] == null) {
|
||||||
|
zapPayments = zapPayments + Pair(zapPaymentRequest, zapPayment)
|
||||||
|
liveSet?.zaps?.invalidateData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun addReaction(note: Note) {
|
fun addReaction(note: Note) {
|
||||||
if (note !in reactions) {
|
if (note !in reactions) {
|
||||||
reactions = reactions + note
|
reactions = reactions + note
|
||||||
@@ -204,6 +229,14 @@ open class Note(val idHex: String) {
|
|||||||
// Zaps who the requester was the user
|
// Zaps who the requester was the user
|
||||||
return zaps.any {
|
return zaps.any {
|
||||||
it.key.author === user || account.decryptZapContentAuthor(it.key)?.pubKey == user.pubkeyHex
|
it.key.author === user || account.decryptZapContentAuthor(it.key)?.pubKey == user.pubkeyHex
|
||||||
|
} || zapPayments.any {
|
||||||
|
val zapResponseEvent = it.value?.event as? LnZapPaymentResponseEvent
|
||||||
|
val response = if (zapResponseEvent != null) {
|
||||||
|
account.decryptZapPaymentResponseEvent(zapResponseEvent)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
response is PayInvoiceSuccessResponse && account.isNIP47Author(zapResponseEvent?.requestAuthor())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,12 +266,44 @@ open class Note(val idHex: String) {
|
|||||||
}.flatten()
|
}.flatten()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun zappedAmount(): BigDecimal {
|
fun zappedAmount(privKey: ByteArray?, walletServicePubkey: ByteArray?): BigDecimal {
|
||||||
return zaps.mapNotNull { it.value?.event }
|
// Regular Zap Receipts
|
||||||
|
val completedZaps = zaps.asSequence()
|
||||||
|
.mapNotNull { it.value?.event }
|
||||||
.filterIsInstance<LnZapEvent>()
|
.filterIsInstance<LnZapEvent>()
|
||||||
.mapNotNull {
|
.filter { it.amount != null }
|
||||||
it.amount
|
.associate {
|
||||||
}.sumOf { it }
|
it.lnInvoice() to it.amount
|
||||||
|
}
|
||||||
|
.toMap()
|
||||||
|
|
||||||
|
val completedPayments = if (privKey != null && walletServicePubkey != null) {
|
||||||
|
// Payments confirmed by the User's Wallet
|
||||||
|
zapPayments
|
||||||
|
.asSequence()
|
||||||
|
.filter {
|
||||||
|
val response = (it.value?.event as? LnZapPaymentResponseEvent)?.response(privKey, walletServicePubkey)
|
||||||
|
response is PayInvoiceSuccessResponse
|
||||||
|
}
|
||||||
|
.associate {
|
||||||
|
val lnInvoice = (it.key.event as? LnZapPaymentRequestEvent)?.lnInvoice(privKey)
|
||||||
|
val amount = try {
|
||||||
|
if (lnInvoice == null) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
LnInvoiceUtil.getAmountInSats(lnInvoice)
|
||||||
|
}
|
||||||
|
} catch (e: java.lang.Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
lnInvoice to amount
|
||||||
|
}
|
||||||
|
.toMap()
|
||||||
|
} else {
|
||||||
|
emptyMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
return (completedZaps + completedPayments).values.filterNotNull().sumOf { it }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hasPledgeBy(user: User): Boolean {
|
fun hasPledgeBy(user: User): Boolean {
|
||||||
|
|||||||
@@ -207,6 +207,7 @@ open class Event(
|
|||||||
.registerTypeAdapter(Event::class.java, EventDeserializer())
|
.registerTypeAdapter(Event::class.java, EventDeserializer())
|
||||||
.registerTypeAdapter(ByteArray::class.java, ByteArraySerializer())
|
.registerTypeAdapter(ByteArray::class.java, ByteArraySerializer())
|
||||||
.registerTypeAdapter(ByteArray::class.java, ByteArrayDeserializer())
|
.registerTypeAdapter(ByteArray::class.java, ByteArrayDeserializer())
|
||||||
|
.registerTypeAdapter(Response::class.java, ResponseDeserializer())
|
||||||
.create()
|
.create()
|
||||||
|
|
||||||
fun fromJson(json: String, lenient: Boolean = false): Event = gson.fromJson(json, Event::class.java).getRefinedEvent(lenient)
|
fun fromJson(json: String, lenient: Boolean = false): Event = gson.fromJson(json, Event::class.java).getRefinedEvent(lenient)
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ class LnZapEvent(
|
|||||||
return content
|
return content
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun lnInvoice() = tags.firstOrNull { it.size > 1 && it[0] == "bolt11" }?.get(1)
|
fun lnInvoice() = tags.firstOrNull { it.size > 1 && it[0] == "bolt11" }?.get(1)
|
||||||
|
|
||||||
private fun description() = tags.firstOrNull { it.size > 1 && it[0] == "description" }?.get(1)
|
private fun description() = tags.firstOrNull { it.size > 1 && it[0] == "description" }?.get(1)
|
||||||
|
|
||||||
|
|||||||
+48
-2
@@ -1,9 +1,14 @@
|
|||||||
package com.vitorpamplona.amethyst.service.model
|
package com.vitorpamplona.amethyst.service.model
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import com.google.gson.JsonDeserializationContext
|
||||||
|
import com.google.gson.JsonDeserializer
|
||||||
|
import com.google.gson.JsonElement
|
||||||
|
import com.google.gson.JsonParseException
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import com.vitorpamplona.amethyst.model.HexKey
|
import com.vitorpamplona.amethyst.model.HexKey
|
||||||
import nostr.postr.Utils
|
import nostr.postr.Utils
|
||||||
|
import java.lang.reflect.Type
|
||||||
|
|
||||||
class LnZapPaymentResponseEvent(
|
class LnZapPaymentResponseEvent(
|
||||||
id: HexKey,
|
id: HexKey,
|
||||||
@@ -35,8 +40,13 @@ class LnZapPaymentResponseEvent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun response(privKey: ByteArray, pubKey: ByteArray): Response? = try {
|
fun response(privKey: ByteArray, pubKey: ByteArray): Response? = try {
|
||||||
|
println("Response Arrived: Decrypting")
|
||||||
if (content.isNotEmpty()) {
|
if (content.isNotEmpty()) {
|
||||||
gson.fromJson(decrypt(privKey, pubKey), Response::class.java)
|
val decrypted = decrypt(privKey, pubKey)
|
||||||
|
println("Response Arrived: Parsing $decrypted")
|
||||||
|
val response = gson.fromJson(decrypted, Response::class.java)
|
||||||
|
println("Response Arrived: Decrypted ${response?.resultType}")
|
||||||
|
response
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
@@ -58,7 +68,7 @@ abstract class Response(
|
|||||||
|
|
||||||
// PayInvoice Call
|
// PayInvoice Call
|
||||||
|
|
||||||
class PayInvoiceSuccessResponse(val result: PayInvoiceResultParams) :
|
class PayInvoiceSuccessResponse(val result: PayInvoiceResultParams? = null) :
|
||||||
Response("pay_invoice") {
|
Response("pay_invoice") {
|
||||||
class PayInvoiceResultParams(val preimage: String)
|
class PayInvoiceResultParams(val preimage: String)
|
||||||
}
|
}
|
||||||
@@ -86,3 +96,39 @@ class PayInvoiceErrorResponse(val error: PayInvoiceErrorParams? = null) :
|
|||||||
OTHER // Other error.
|
OTHER // Other error.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ResponseDeserializer :
|
||||||
|
JsonDeserializer<Response?> {
|
||||||
|
@Throws(JsonParseException::class)
|
||||||
|
override fun deserialize(
|
||||||
|
json: JsonElement,
|
||||||
|
typeOfT: Type,
|
||||||
|
context: JsonDeserializationContext
|
||||||
|
): Response? {
|
||||||
|
val jsonObject = json.asJsonObject
|
||||||
|
val resultType = jsonObject.get("result_type")?.asString
|
||||||
|
|
||||||
|
if (resultType == "pay_invoice") {
|
||||||
|
val result = jsonObject.get("result")?.asJsonObject
|
||||||
|
val error = jsonObject.get("error")?.asJsonObject
|
||||||
|
if (result != null) {
|
||||||
|
return context.deserialize<PayInvoiceSuccessResponse>(jsonObject, PayInvoiceSuccessResponse::class.java)
|
||||||
|
}
|
||||||
|
if (error != null) {
|
||||||
|
return context.deserialize<PayInvoiceErrorResponse>(jsonObject, PayInvoiceErrorResponse::class.java)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// tries to guess
|
||||||
|
if (jsonObject.get("result")?.asJsonObject?.get("preimage") != null) {
|
||||||
|
return context.deserialize<PayInvoiceSuccessResponse>(jsonObject, PayInvoiceSuccessResponse::class.java)
|
||||||
|
}
|
||||||
|
if (jsonObject.get("error")?.asJsonObject?.get("code") != null) {
|
||||||
|
return context.deserialize<PayInvoiceErrorResponse>(jsonObject, PayInvoiceErrorResponse::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -56,8 +56,7 @@ fun PollNote(
|
|||||||
val account = accountState?.account ?: return
|
val account = accountState?.account ?: return
|
||||||
|
|
||||||
val pollViewModel = PollNoteViewModel()
|
val pollViewModel = PollNoteViewModel()
|
||||||
pollViewModel.account = account
|
pollViewModel.load(account, zappedNote)
|
||||||
pollViewModel.load(zappedNote)
|
|
||||||
|
|
||||||
pollViewModel.pollEvent?.pollOptions()?.forEach { poll_op ->
|
pollViewModel.pollEvent?.pollOptions()?.forEach { poll_op ->
|
||||||
OptionNote(
|
OptionNote(
|
||||||
@@ -212,7 +211,7 @@ fun ZapVote(
|
|||||||
clickablePrepend: @Composable () -> Unit
|
clickablePrepend: @Composable () -> Unit
|
||||||
) {
|
) {
|
||||||
val zapsState by baseNote.live().zaps.observeAsState()
|
val zapsState by baseNote.live().zaps.observeAsState()
|
||||||
val zappedNote = zapsState?.note
|
val zappedNote = zapsState?.note ?: return
|
||||||
|
|
||||||
var wantsToZap by remember { mutableStateOf(false) }
|
var wantsToZap by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
@@ -253,7 +252,7 @@ fun ZapVote(
|
|||||||
)
|
)
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
} else if (accountViewModel.isLoggedUser(zappedNote?.author)) {
|
} else if (accountViewModel.isLoggedUser(zappedNote.author)) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
Toast
|
Toast
|
||||||
.makeText(
|
.makeText(
|
||||||
@@ -376,7 +375,7 @@ fun ZapVote(
|
|||||||
LaunchedEffect(key1 = zapsState) {
|
LaunchedEffect(key1 = zapsState) {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
if (!wasZappedByLoggedInUser) {
|
if (!wasZappedByLoggedInUser) {
|
||||||
wasZappedByLoggedInUser = zappedNote?.isZappedBy(accountViewModel.userProfile(), account) == true
|
wasZappedByLoggedInUser = accountViewModel.calculateIfNoteWasZappedByAccount(zappedNote)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,8 +20,10 @@ class PollNoteViewModel {
|
|||||||
var consensusThreshold: BigDecimal? = null
|
var consensusThreshold: BigDecimal? = null
|
||||||
|
|
||||||
var totalZapped: BigDecimal = BigDecimal.ZERO
|
var totalZapped: BigDecimal = BigDecimal.ZERO
|
||||||
|
var wasZappedByAuthor: Boolean = false
|
||||||
|
|
||||||
fun load(note: Note?) {
|
fun load(acc: Account, note: Note?) {
|
||||||
|
account = acc
|
||||||
pollNote = note
|
pollNote = note
|
||||||
pollEvent = pollNote?.event as PollNoteEvent
|
pollEvent = pollNote?.event as PollNoteEvent
|
||||||
pollOptions = pollEvent?.pollOptions()
|
pollOptions = pollEvent?.pollOptions()
|
||||||
@@ -31,13 +33,14 @@ class PollNoteViewModel {
|
|||||||
closedAt = pollEvent?.getTagInt(CLOSED_AT)
|
closedAt = pollEvent?.getTagInt(CLOSED_AT)
|
||||||
|
|
||||||
totalZapped = totalZapped()
|
totalZapped = totalZapped()
|
||||||
|
wasZappedByAuthor = note?.let { account?.calculateIfNoteWasZappedByAccount(it) } ?: false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun canZap(): Boolean {
|
fun canZap(): Boolean {
|
||||||
val account = account ?: return false
|
val account = account ?: return false
|
||||||
val user = account.userProfile() ?: return false
|
val user = account.userProfile() ?: return false
|
||||||
val note = pollNote ?: return false
|
val note = pollNote ?: return false
|
||||||
return user != note.author && !note.isZappedBy(user, account)
|
return user != note.author && !wasZappedByAuthor
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isVoteAmountAtomic() = valueMaximum != null && valueMinimum != null && valueMinimum == valueMaximum
|
fun isVoteAmountAtomic() = valueMaximum != null && valueMinimum != null && valueMinimum == valueMaximum
|
||||||
|
|||||||
@@ -309,7 +309,7 @@ fun ZapReaction(
|
|||||||
val account = accountState?.account ?: return
|
val account = accountState?.account ?: return
|
||||||
|
|
||||||
val zapsState by baseNote.live().zaps.observeAsState()
|
val zapsState by baseNote.live().zaps.observeAsState()
|
||||||
val zappedNote = zapsState?.note
|
val zappedNote = zapsState?.note ?: return
|
||||||
val zapMessage = ""
|
val zapMessage = ""
|
||||||
|
|
||||||
var wantsToZap by remember { mutableStateOf(false) }
|
var wantsToZap by remember { mutableStateOf(false) }
|
||||||
@@ -326,7 +326,7 @@ fun ZapReaction(
|
|||||||
LaunchedEffect(key1 = zapsState) {
|
LaunchedEffect(key1 = zapsState) {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
if (!wasZappedByLoggedInUser) {
|
if (!wasZappedByLoggedInUser) {
|
||||||
wasZappedByLoggedInUser = zappedNote?.isZappedBy(account.userProfile(), account) == true
|
wasZappedByLoggedInUser = accountViewModel.calculateIfNoteWasZappedByAccount(zappedNote)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -459,7 +459,7 @@ fun ZapReaction(
|
|||||||
|
|
||||||
LaunchedEffect(key1 = zapsState) {
|
LaunchedEffect(key1 = zapsState) {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
zapAmount = zappedNote?.zappedAmount()
|
zapAmount = account.calculateZappedAmount(zappedNote)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,10 @@ class AccountViewModel(private val account: Account) : ViewModel() {
|
|||||||
zap(note, amount, pollOption, message, context, onError, onProgress, account.defaultZapType)
|
zap(note, amount, pollOption, message, context, onError, onProgress, account.defaultZapType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun calculateIfNoteWasZappedByAccount(zappedNote: Note): Boolean {
|
||||||
|
return account.calculateIfNoteWasZappedByAccount(zappedNote)
|
||||||
|
}
|
||||||
|
|
||||||
fun zap(note: Note, amount: Long, pollOption: Int?, message: String, context: Context, onError: (String) -> Unit, onProgress: (percent: Float) -> Unit, zapType: LnZapEvent.ZapType) {
|
fun zap(note: Note, amount: Long, pollOption: Int?, message: String, context: Context, onError: (String) -> Unit, onProgress: (percent: Float) -> Unit, zapType: LnZapEvent.ZapType) {
|
||||||
val lud16 = note.event?.zapAddress() ?: note.author?.info?.lud16?.trim() ?: note.author?.info?.lud06?.trim()
|
val lud16 = note.event?.zapAddress() ?: note.author?.info?.lud16?.trim() ?: note.author?.info?.lud06?.trim()
|
||||||
|
|
||||||
@@ -88,6 +92,7 @@ class AccountViewModel(private val account: Account) : ViewModel() {
|
|||||||
if (account.hasWalletConnectSetup()) {
|
if (account.hasWalletConnectSetup()) {
|
||||||
account.sendZapPaymentRequestFor(
|
account.sendZapPaymentRequestFor(
|
||||||
bolt11 = it,
|
bolt11 = it,
|
||||||
|
note,
|
||||||
onResponse = { response ->
|
onResponse = { response ->
|
||||||
if (response is PayInvoiceErrorResponse) {
|
if (response is PayInvoiceErrorResponse) {
|
||||||
onProgress(0.0f)
|
onProgress(0.0f)
|
||||||
|
|||||||
@@ -565,7 +565,7 @@ private fun DrawAdditionalInfo(baseUser: User, account: Account, accountViewMode
|
|||||||
onSuccess = {
|
onSuccess = {
|
||||||
// pay directly
|
// pay directly
|
||||||
if (account.hasWalletConnectSetup()) {
|
if (account.hasWalletConnectSetup()) {
|
||||||
account.sendZapPaymentRequestFor(it) { response ->
|
account.sendZapPaymentRequestFor(it, null) { response ->
|
||||||
if (response is PayInvoiceSuccessResponse) {
|
if (response is PayInvoiceSuccessResponse) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
|
|||||||
Reference in New Issue
Block a user