fix reaction row and zaps

This commit is contained in:
greenart7c3
2023-09-13 15:54:44 -03:00
parent a2fcb3539e
commit e00fc83e5c
7 changed files with 420 additions and 124 deletions
@@ -405,37 +405,30 @@ class Account(
val emojiUrl = EmojiUrl.decode(reaction) val emojiUrl = EmojiUrl.decode(reaction)
if (emojiUrl != null) { if (emojiUrl != null) {
note.event?.let { note.event?.let {
var event = ReactionEvent.create(emojiUrl, it, keyPair) val event = ReactionEvent.create(emojiUrl, it, keyPair)
if (loginWithAmber) { if (loginWithAmber) {
AmberUtils.openAmber(event) AmberUtils.signEvent(event)
if (AmberUtils.content.isBlank()) { } else {
return
}
event = ReactionEvent.create(event, AmberUtils.content)
}
Client.send(event) Client.send(event)
LocalCache.consume(event) LocalCache.consume(event)
} }
}
return return
} }
} }
note.event?.let { note.event?.let {
var event = ReactionEvent.create(reaction, it, keyPair) val event = ReactionEvent.create(reaction, it, keyPair)
if (loginWithAmber) { if (loginWithAmber) {
AmberUtils.openAmber(event) AmberUtils.signEvent(event)
if (AmberUtils.content.isBlank()) { } else {
return
}
event = ReactionEvent.create(event, AmberUtils.content)
}
Client.send(event) Client.send(event)
LocalCache.consume(event) LocalCache.consume(event)
} }
} }
} }
}
fun createZapRequestFor(note: Note, pollOption: Int?, message: String = "", zapType: LnZapEvent.ZapType): LnZapRequestEvent? { fun createZapRequestFor(note: Note, pollOption: Int?, message: String = "", zapType: LnZapEvent.ZapType): LnZapRequestEvent? {
if (!isWriteable() && !loginWithAmber) return null if (!isWriteable() && !loginWithAmber) return null
@@ -453,7 +446,7 @@ class Account(
) )
} }
LnZapEvent.ZapType.PUBLIC -> { LnZapEvent.ZapType.PUBLIC -> {
val unsignedEvent = LnZapRequestEvent.createPublic( return LnZapRequestEvent.createPublic(
event, event,
userProfile().latestContactList?.relays()?.keys?.ifEmpty { null } userProfile().latestContactList?.relays()?.keys?.ifEmpty { null }
?: localRelays.map { it.url }.toSet(), ?: localRelays.map { it.url }.toSet(),
@@ -461,21 +454,10 @@ class Account(
pollOption, pollOption,
message message
) )
AmberUtils.content = ""
AmberUtils.openAmber(unsignedEvent)
if (AmberUtils.content.isBlank()) return null
return LnZapRequestEvent(
unsignedEvent.id,
unsignedEvent.pubKey,
unsignedEvent.createdAt,
unsignedEvent.tags,
unsignedEvent.content,
AmberUtils.content
)
} }
LnZapEvent.ZapType.PRIVATE -> { LnZapEvent.ZapType.PRIVATE -> {
val unsignedEvent = LnZapRequestEvent.createPrivateZap( return LnZapRequestEvent.createPrivateZap(
event, event,
userProfile().latestContactList?.relays()?.keys?.ifEmpty { null } userProfile().latestContactList?.relays()?.keys?.ifEmpty { null }
?: localRelays.map { it.url }.toSet(), ?: localRelays.map { it.url }.toSet(),
@@ -483,10 +465,6 @@ class Account(
pollOption, pollOption,
message message
) )
AmberUtils.content = ""
AmberUtils.openAmber(unsignedEvent)
if (AmberUtils.content.isBlank()) return null
return Event.fromJson(AmberUtils.content) as LnZapRequestEvent
} }
else -> null else -> null
} }
@@ -674,18 +652,15 @@ class Account(
val myNotes = notes.filter { it.author == userProfile() }.map { it.idHex } val myNotes = notes.filter { it.author == userProfile() }.map { it.idHex }
if (myNotes.isNotEmpty()) { if (myNotes.isNotEmpty()) {
var event = DeletionEvent.create(myNotes, keyPair) val event = DeletionEvent.create(myNotes, keyPair)
if (loginWithAmber) { if (loginWithAmber) {
AmberUtils.openAmber(event) AmberUtils.signEvent(event)
if (AmberUtils.content.isBlank()) { } else {
return
}
event = DeletionEvent.create(event, AmberUtils.content)
}
Client.send(event) Client.send(event)
LocalCache.consume(event) LocalCache.consume(event)
} }
} }
}
fun createHTTPAuthorization(url: String, method: String, body: String? = null): HTTPAuthorizationEvent? { fun createHTTPAuthorization(url: String, method: String, body: String? = null): HTTPAuthorizationEvent? {
if (!isWriteable() && !loginWithAmber) return null if (!isWriteable() && !loginWithAmber) return null
@@ -711,28 +686,22 @@ class Account(
note.event?.let { note.event?.let {
if (it.kind() == 1) { if (it.kind() == 1) {
var event = RepostEvent.create(it, keyPair) val event = RepostEvent.create(it, keyPair)
if (loginWithAmber) { if (loginWithAmber) {
AmberUtils.openAmber(event) AmberUtils.signEvent(event)
if (AmberUtils.content.isBlank()) {
return
}
event = RepostEvent.create(event, AmberUtils.content)
}
Client.send(event)
LocalCache.consume(event)
} else { } else {
var event = GenericRepostEvent.create(it, keyPair)
if (loginWithAmber) {
AmberUtils.openAmber(event)
if (AmberUtils.content.isBlank()) {
return
}
event = GenericRepostEvent.create(event, AmberUtils.content)
}
Client.send(event) Client.send(event)
LocalCache.consume(event) LocalCache.consume(event)
} }
} else {
val event = GenericRepostEvent.create(it, keyPair)
if (loginWithAmber) {
AmberUtils.signEvent(event)
} else {
Client.send(event)
LocalCache.consume(event)
}
}
} }
} }
@@ -7,6 +7,7 @@ import com.vitorpamplona.amethyst.ServiceManager
import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.ui.actions.SignerType import com.vitorpamplona.amethyst.ui.actions.SignerType
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.encoders.toHexKey
import com.vitorpamplona.quartz.events.Event import com.vitorpamplona.quartz.events.Event
import com.vitorpamplona.quartz.events.EventInterface import com.vitorpamplona.quartz.events.EventInterface
import com.vitorpamplona.quartz.events.LnZapRequestEvent import com.vitorpamplona.quartz.events.LnZapRequestEvent
@@ -54,18 +55,20 @@ object AmberUtils {
event.id() event.id()
) )
while (isActivityRunning) { while (isActivityRunning) {
Thread.sleep(100) // do nothing
} }
} }
fun loginWithAmber() { fun signEvent(event: EventInterface) {
checkNotInMainThread() checkNotInMainThread()
ServiceManager.shouldPauseService = false
isActivityRunning = true
openAmber( openAmber(
"", event.toJson(),
SignerType.GET_PUBLIC_KEY, SignerType.SIGN_EVENT,
IntentUtils.activityResultLauncher, IntentUtils.signEventResultLauncher,
"", account.keyPair.pubKey.toHexKey(),
"" event.id()
) )
} }
@@ -9,6 +9,7 @@ import androidx.activity.result.contract.ActivityResultContracts
import com.vitorpamplona.amethyst.Amethyst import com.vitorpamplona.amethyst.Amethyst
import com.vitorpamplona.amethyst.ServiceManager import com.vitorpamplona.amethyst.ServiceManager
import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.service.relays.Client
import com.vitorpamplona.amethyst.ui.MainActivity import com.vitorpamplona.amethyst.ui.MainActivity
import com.vitorpamplona.quartz.events.Event import com.vitorpamplona.quartz.events.Event
import com.vitorpamplona.quartz.events.GiftWrapEvent import com.vitorpamplona.quartz.events.GiftWrapEvent
@@ -22,6 +23,7 @@ object IntentUtils {
lateinit var activityResultLauncher: ActivityResultLauncher<Intent> lateinit var activityResultLauncher: ActivityResultLauncher<Intent>
lateinit var decryptGossipResultLauncher: ActivityResultLauncher<Intent> lateinit var decryptGossipResultLauncher: ActivityResultLauncher<Intent>
lateinit var blockListResultLauncher: ActivityResultLauncher<Intent> lateinit var blockListResultLauncher: ActivityResultLauncher<Intent>
lateinit var signEventResultLauncher: ActivityResultLauncher<Intent>
val eventCache = LruCache<String, Event>(100) val eventCache = LruCache<String, Event>(100)
@OptIn(DelicateCoroutinesApi::class) @OptIn(DelicateCoroutinesApi::class)
@@ -63,6 +65,31 @@ object IntentUtils {
@OptIn(DelicateCoroutinesApi::class) @OptIn(DelicateCoroutinesApi::class)
fun start(activity: MainActivity) { fun start(activity: MainActivity) {
signEventResultLauncher = activity.registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
if (it.resultCode != Activity.RESULT_OK) {
GlobalScope.launch(Dispatchers.Main) {
Toast.makeText(
Amethyst.instance,
"Sign request rejected",
Toast.LENGTH_SHORT
).show()
}
} else {
val json = it.data?.getStringExtra("event") ?: ""
GlobalScope.launch(Dispatchers.IO) {
val signedEvent = Event.fromJson(json)
if (signedEvent.hasValidSignature()) {
Client.send(signedEvent)
LocalCache.verifyAndConsume(signedEvent, null)
}
}
}
AmberUtils.isActivityRunning = false
ServiceManager.shouldPauseService = true
}
activityResultLauncher = activity.registerForActivityResult( activityResultLauncher = activity.registerForActivityResult(
ActivityResultContracts.StartActivityForResult() ActivityResultContracts.StartActivityForResult()
) { ) {
@@ -1,6 +1,9 @@
package com.vitorpamplona.amethyst.ui.note package com.vitorpamplona.amethyst.ui.note
import android.app.Activity
import android.widget.Toast import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.* import androidx.compose.foundation.*
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.material.* import androidx.compose.material.*
@@ -22,8 +25,12 @@ import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Popup import androidx.compose.ui.window.Popup
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.Amethyst
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ServiceManager
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.service.AmberUtils
import com.vitorpamplona.amethyst.ui.actions.SignerType
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
@@ -32,8 +39,10 @@ import com.vitorpamplona.amethyst.ui.theme.Font14SP
import com.vitorpamplona.amethyst.ui.theme.QuoteBorder import com.vitorpamplona.amethyst.ui.theme.QuoteBorder
import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.quartz.events.Event
import com.vitorpamplona.quartz.events.ImmutableListOfLists import com.vitorpamplona.quartz.events.ImmutableListOfLists
import com.vitorpamplona.quartz.events.LnZapEvent import com.vitorpamplona.quartz.events.LnZapEvent
import com.vitorpamplona.quartz.events.LnZapRequestEvent
import com.vitorpamplona.quartz.events.toImmutableListOfLists import com.vitorpamplona.quartz.events.toImmutableListOfLists
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -291,6 +300,60 @@ fun ZapVote(
nonClickablePrepend() nonClickablePrepend()
val event = remember { mutableStateOf<LnZapRequestEvent?>(null) }
val activityResult = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult(),
onResult = {
if (it.resultCode != Activity.RESULT_OK) {
scope.launch(Dispatchers.Main) {
Toast.makeText(
Amethyst.instance,
"Sign request rejected",
Toast.LENGTH_SHORT
).show()
}
} else {
val json = it.data?.getStringExtra("event") ?: ""
val signedEvent = Event.fromJson(json) as LnZapRequestEvent
if (signedEvent.hasValidSignature()) {
accountViewModel.zap(
baseNote,
accountViewModel.account.zapAmountChoices.first() * 1000,
null,
"",
context,
{
scope.launch {
zappingProgress = 0f
showErrorMessageDialog = it
}
},
{ progress: Float ->
zappingProgress = progress
},
accountViewModel.account.defaultZapType,
null
)
}
}
AmberUtils.isActivityRunning = false
ServiceManager.shouldPauseService = true
event.value = null
}
)
LaunchedEffect(event.value) {
if (event.value != null) {
AmberUtils.openAmber(
event.value!!.toJson(),
SignerType.SIGN_EVENT,
activityResult,
"",
event.value!!.id()
)
}
}
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.combinedClickable( modifier = Modifier.combinedClickable(
@@ -362,7 +425,8 @@ fun ZapVote(
zappingProgress = it zappingProgress = it
} }
}, },
zapType = accountViewModel.account.defaultZapType zapType = accountViewModel.account.defaultZapType,
null
) )
} else { } else {
wantsToZap = true wantsToZap = true
@@ -502,7 +566,8 @@ fun FilteredZapAmountChoicePopup(
context, context,
onError, onError,
onProgress, onProgress,
defaultZapType defaultZapType,
null
) )
onDismiss() onDismiss()
}, },
@@ -526,7 +591,8 @@ fun FilteredZapAmountChoicePopup(
context, context,
onError, onError,
onProgress, onProgress,
defaultZapType defaultZapType,
null
) )
onDismiss() onDismiss()
}, },
@@ -1,8 +1,11 @@
package com.vitorpamplona.amethyst.ui.note package com.vitorpamplona.amethyst.ui.note
import android.app.Activity
import android.content.Context import android.content.Context
import android.util.Log import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedContentTransitionScope import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.ContentTransform import androidx.compose.animation.ContentTransform
@@ -46,6 +49,7 @@ import androidx.compose.runtime.MutableState
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableLongStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
@@ -76,9 +80,13 @@ import androidx.lifecycle.map
import coil.compose.AsyncImage import coil.compose.AsyncImage
import coil.request.CachePolicy import coil.request.CachePolicy
import coil.request.ImageRequest import coil.request.ImageRequest
import com.vitorpamplona.amethyst.Amethyst
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ServiceManager
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.service.AmberUtils
import com.vitorpamplona.amethyst.ui.actions.NewPostView import com.vitorpamplona.amethyst.ui.actions.NewPostView
import com.vitorpamplona.amethyst.ui.actions.SignerType
import com.vitorpamplona.amethyst.ui.components.ImageUrlType import com.vitorpamplona.amethyst.ui.components.ImageUrlType
import com.vitorpamplona.amethyst.ui.components.InLineIconRenderer import com.vitorpamplona.amethyst.ui.components.InLineIconRenderer
import com.vitorpamplona.amethyst.ui.components.TextType import com.vitorpamplona.amethyst.ui.components.TextType
@@ -107,6 +115,9 @@ import com.vitorpamplona.amethyst.ui.theme.TinyBorders
import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.amethyst.ui.theme.placeholderTextColorFilter import com.vitorpamplona.amethyst.ui.theme.placeholderTextColorFilter
import com.vitorpamplona.quartz.events.Event
import com.vitorpamplona.quartz.events.LnZapEvent
import com.vitorpamplona.quartz.events.LnZapRequestEvent
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toImmutableMap import kotlinx.collections.immutable.toImmutableMap
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@@ -951,6 +962,63 @@ fun ZapReaction(
var zappingProgress by remember { mutableStateOf(0f) } var zappingProgress by remember { mutableStateOf(0f) }
val event = remember { mutableStateOf<LnZapRequestEvent?>(null) }
val activityResult = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult(),
onResult = {
if (it.resultCode != Activity.RESULT_OK) {
wantsToZap = false
zappingProgress = 0f
scope.launch(Dispatchers.Main) {
Toast.makeText(
Amethyst.instance,
"Sign request rejected",
Toast.LENGTH_SHORT
).show()
}
} else {
val json = it.data?.getStringExtra("event") ?: ""
val signedEvent = Event.fromJson(json) as LnZapRequestEvent
if (signedEvent.hasValidSignature()) {
accountViewModel.zap(
baseNote,
accountViewModel.account.zapAmountChoices.first() * 1000,
null,
"",
context,
{
scope.launch {
zappingProgress = 0f
showErrorMessageDialog = it
}
},
{ progress: Float ->
zappingProgress = progress
},
accountViewModel.account.defaultZapType,
signedEvent
)
}
}
AmberUtils.isActivityRunning = false
ServiceManager.shouldPauseService = true
event.value = null
wantsToZap = false
}
)
LaunchedEffect(event.value) {
if (event.value != null) {
AmberUtils.openAmber(
event.value!!.toJson(),
SignerType.SIGN_EVENT,
activityResult,
"",
event.value!!.id()
)
}
}
Row( Row(
verticalAlignment = CenterVertically, verticalAlignment = CenterVertically,
modifier = Modifier modifier = Modifier
@@ -976,7 +1044,8 @@ fun ZapReaction(
zappingProgress = 0f zappingProgress = 0f
showErrorMessageDialog = it showErrorMessageDialog = it
} }
} },
event
) )
}, },
onLongClick = { onLongClick = {
@@ -1083,7 +1152,8 @@ private fun zapClick(
context: Context, context: Context,
onZappingProgress: (Float) -> Unit, onZappingProgress: (Float) -> Unit,
onMultipleChoices: () -> Unit, onMultipleChoices: () -> Unit,
onError: (String) -> Unit onError: (String) -> Unit,
event: MutableState<LnZapRequestEvent?>
) { ) {
if (accountViewModel.account.zapAmountChoices.isEmpty()) { if (accountViewModel.account.zapAmountChoices.isEmpty()) {
scope.launch { scope.launch {
@@ -1098,6 +1168,14 @@ private fun zapClick(
} else if (!accountViewModel.isWriteable()) { } else if (!accountViewModel.isWriteable()) {
if (accountViewModel.loggedInWithAmber()) { if (accountViewModel.loggedInWithAmber()) {
if (accountViewModel.account.zapAmountChoices.size == 1) { if (accountViewModel.account.zapAmountChoices.size == 1) {
if (accountViewModel.account.defaultZapType != LnZapEvent.ZapType.ANONYMOUS && accountViewModel.account.defaultZapType != LnZapEvent.ZapType.NONZAP) {
event.value = accountViewModel.account.createZapRequestFor(
baseNote,
null,
"",
accountViewModel.account.defaultZapType
)
} else {
accountViewModel.zap( accountViewModel.zap(
baseNote, baseNote,
accountViewModel.account.zapAmountChoices.first() * 1000, accountViewModel.account.zapAmountChoices.first() * 1000,
@@ -1110,8 +1188,10 @@ private fun zapClick(
onZappingProgress(it) onZappingProgress(it)
} }
}, },
zapType = accountViewModel.account.defaultZapType zapType = accountViewModel.account.defaultZapType,
null
) )
}
} else if (accountViewModel.account.zapAmountChoices.size > 1) { } else if (accountViewModel.account.zapAmountChoices.size > 1) {
onMultipleChoices() onMultipleChoices()
} }
@@ -1139,7 +1219,8 @@ private fun zapClick(
onZappingProgress(it) onZappingProgress(it)
} }
}, },
zapType = accountViewModel.account.defaultZapType zapType = accountViewModel.account.defaultZapType,
null
) )
} else if (accountViewModel.account.zapAmountChoices.size > 1) { } else if (accountViewModel.account.zapAmountChoices.size > 1) {
onMultipleChoices() onMultipleChoices()
@@ -1458,10 +1539,58 @@ fun ZapAmountChoicePopup(
onProgress: (percent: Float) -> Unit onProgress: (percent: Float) -> Unit
) { ) {
val context = LocalContext.current val context = LocalContext.current
val scope = rememberCoroutineScope()
val accountState by accountViewModel.accountLiveData.observeAsState() val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account ?: return val account = accountState?.account ?: return
val zapMessage = "" val zapMessage = ""
var event by remember { mutableStateOf<LnZapRequestEvent?>(null) }
var amount by remember { mutableLongStateOf(0L) }
val activityResult = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult(),
onResult = {
if (it.resultCode != Activity.RESULT_OK) {
scope.launch(Dispatchers.Main) {
Toast.makeText(
Amethyst.instance,
"Sign request rejected",
Toast.LENGTH_SHORT
).show()
}
} else {
val json = it.data?.getStringExtra("event") ?: ""
val signedEvent = Event.fromJson(json) as LnZapRequestEvent
if (signedEvent.hasValidSignature()) {
accountViewModel.zap(
baseNote,
amount,
null,
zapMessage,
context,
onError,
onProgress,
account.defaultZapType,
signedEvent
)
amount = 0
}
}
AmberUtils.isActivityRunning = false
ServiceManager.shouldPauseService = true
event = null
}
)
LaunchedEffect(event) {
if (event != null) {
AmberUtils.openAmber(
event!!.toJson(),
SignerType.SIGN_EVENT,
activityResult,
"",
event!!.id()
)
}
}
Popup( Popup(
alignment = Alignment.BottomCenter, alignment = Alignment.BottomCenter,
@@ -1473,6 +1602,10 @@ fun ZapAmountChoicePopup(
Button( Button(
modifier = Modifier.padding(horizontal = 3.dp), modifier = Modifier.padding(horizontal = 3.dp),
onClick = { onClick = {
if (accountViewModel.loggedInWithAmber() && account.defaultZapType != LnZapEvent.ZapType.NONZAP && account.defaultZapType != LnZapEvent.ZapType.ANONYMOUS) {
amount = amountInSats * 1000
event = account.createZapRequestFor(baseNote, null, zapMessage, account.defaultZapType)
} else {
accountViewModel.zap( accountViewModel.zap(
baseNote, baseNote,
amountInSats * 1000, amountInSats * 1000,
@@ -1481,9 +1614,11 @@ fun ZapAmountChoicePopup(
context, context,
onError, onError,
onProgress, onProgress,
account.defaultZapType account.defaultZapType,
null
) )
onDismiss() onDismiss()
}
}, },
shape = ButtonBorder, shape = ButtonBorder,
colors = ButtonDefaults colors = ButtonDefaults
@@ -1497,6 +1632,10 @@ fun ZapAmountChoicePopup(
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
modifier = Modifier.combinedClickable( modifier = Modifier.combinedClickable(
onClick = { onClick = {
if (accountViewModel.loggedInWithAmber() && account.defaultZapType != LnZapEvent.ZapType.NONZAP && account.defaultZapType != LnZapEvent.ZapType.ANONYMOUS) {
amount = amountInSats * 1000
event = account.createZapRequestFor(baseNote, null, zapMessage, account.defaultZapType)
} else {
accountViewModel.zap( accountViewModel.zap(
baseNote, baseNote,
amountInSats * 1000, amountInSats * 1000,
@@ -1505,9 +1644,11 @@ fun ZapAmountChoicePopup(
context, context,
onError, onError,
onProgress, onProgress,
account.defaultZapType account.defaultZapType,
null
) )
onDismiss() onDismiss()
}
}, },
onLongClick = { onLongClick = {
onChangeAmount() onChangeAmount()
@@ -1,5 +1,9 @@
package com.vitorpamplona.amethyst.ui.note package com.vitorpamplona.amethyst.ui.note
import android.app.Activity
import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.* import androidx.compose.material.*
@@ -19,17 +23,26 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties import androidx.compose.ui.window.DialogProperties
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.Amethyst
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ServiceManager
import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.service.AmberUtils
import com.vitorpamplona.amethyst.ui.actions.CloseButton import com.vitorpamplona.amethyst.ui.actions.CloseButton
import com.vitorpamplona.amethyst.ui.actions.SignerType
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.TextSpinner import com.vitorpamplona.amethyst.ui.screen.loggedIn.TextSpinner
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.quartz.events.Event
import com.vitorpamplona.quartz.events.LnZapEvent import com.vitorpamplona.quartz.events.LnZapEvent
import com.vitorpamplona.quartz.events.LnZapRequestEvent
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class ZapOptionstViewModel : ViewModel() { class ZapOptionstViewModel : ViewModel() {
private var account: Account? = null private var account: Account? = null
@@ -83,6 +96,55 @@ fun ZapCustomDialog(
val zapOptionExplainers = remember { zapTypes.map { it.third }.toImmutableList() } val zapOptionExplainers = remember { zapTypes.map { it.third }.toImmutableList() }
var selectedZapType by remember(accountViewModel) { mutableStateOf(accountViewModel.account.defaultZapType) } var selectedZapType by remember(accountViewModel) { mutableStateOf(accountViewModel.account.defaultZapType) }
val event = remember { mutableStateOf<LnZapRequestEvent?>(null) }
val activityResult = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult(),
onResult = {
if (it.resultCode != Activity.RESULT_OK) {
postViewModel.viewModelScope.launch(Dispatchers.Main) {
Toast.makeText(
Amethyst.instance,
"Sign request rejected",
Toast.LENGTH_SHORT
).show()
}
event.value = null
} else {
val json = it.data?.getStringExtra("event") ?: ""
val signedEvent = Event.fromJson(json) as LnZapRequestEvent
if (signedEvent.hasValidSignature()) {
accountViewModel.zap(
baseNote,
postViewModel.value()!! * 1000L,
null,
postViewModel.customMessage.text,
context,
onError,
onProgress,
selectedZapType,
signedEvent
)
event.value = null
onClose()
}
}
AmberUtils.isActivityRunning = false
ServiceManager.shouldPauseService = true
}
)
LaunchedEffect(event.value) {
if (event.value != null) {
AmberUtils.openAmber(
event.value!!.toJson(),
SignerType.SIGN_EVENT,
activityResult,
"",
event.value!!.id()
)
}
}
Dialog( Dialog(
onDismissRequest = { onClose() }, onDismissRequest = { onClose() },
properties = DialogProperties( properties = DialogProperties(
@@ -105,6 +167,9 @@ fun ZapCustomDialog(
ZapButton( ZapButton(
isActive = postViewModel.canSend() isActive = postViewModel.canSend()
) { ) {
if (accountViewModel.loggedInWithAmber() && selectedZapType != LnZapEvent.ZapType.ANONYMOUS && selectedZapType != LnZapEvent.ZapType.PUBLIC) {
event.value = accountViewModel.account.createZapRequestFor(baseNote, null, postViewModel.customMessage.text, selectedZapType)
} else {
accountViewModel.zap( accountViewModel.zap(
baseNote, baseNote,
postViewModel.value()!! * 1000L, postViewModel.value()!! * 1000L,
@@ -113,11 +178,13 @@ fun ZapCustomDialog(
context, context,
onError = onError, onError = onError,
onProgress = onProgress, onProgress = onProgress,
zapType = selectedZapType zapType = selectedZapType,
null
) )
onClose() onClose()
} }
} }
}
Row( Row(
modifier = Modifier modifier = Modifier
@@ -217,13 +217,33 @@ class AccountViewModel(val account: Account) : ViewModel() {
return null return null
} }
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,
zapRequest: LnZapRequestEvent?
) {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
innerZap(note, amount, pollOption, message, context, onError, onProgress, zapType) innerZap(note, amount, pollOption, message, context, onError, onProgress, zapType, zapRequest)
} }
} }
private suspend fun innerZap(note: Note, amount: Long, pollOption: Int?, message: String, context: Context, onError: (String) -> Unit, onProgress: (percent: Float) -> Unit, zapType: LnZapEvent.ZapType) { private suspend fun innerZap(
note: Note,
amount: Long,
pollOption: Int?,
message: String,
context: Context,
onError: (String) -> Unit,
onProgress: (percent: Float) -> Unit,
zapType: LnZapEvent.ZapType,
zapRequest: LnZapRequestEvent?
) {
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()
if (lud16.isNullOrBlank()) { if (lud16.isNullOrBlank()) {
@@ -232,11 +252,14 @@ class AccountViewModel(val account: Account) : ViewModel() {
} }
var zapRequestJson = "" var zapRequestJson = ""
if (zapType != LnZapEvent.ZapType.NONZAP) { if (zapType != LnZapEvent.ZapType.NONZAP) {
val zapRequest = account.createZapRequestFor(note, pollOption, message, zapType)
if (zapRequest != null) { if (zapRequest != null) {
zapRequestJson = zapRequest.toJson() zapRequestJson = zapRequest.toJson()
} else {
val localZapRequest = account.createZapRequestFor(note, pollOption, message, zapType)
if (localZapRequest != null) {
zapRequestJson = localZapRequest.toJson()
}
} }
} }