mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-09-26 23:46:28 +02:00
Support for Wallet Connect Api
This commit is contained in:
@@ -8,6 +8,7 @@ import com.google.gson.reflect.TypeToken
|
|||||||
import com.vitorpamplona.amethyst.model.Account
|
import com.vitorpamplona.amethyst.model.Account
|
||||||
import com.vitorpamplona.amethyst.model.RelaySetupInfo
|
import com.vitorpamplona.amethyst.model.RelaySetupInfo
|
||||||
import com.vitorpamplona.amethyst.model.toByteArray
|
import com.vitorpamplona.amethyst.model.toByteArray
|
||||||
|
import com.vitorpamplona.amethyst.service.model.Contact
|
||||||
import com.vitorpamplona.amethyst.service.model.ContactListEvent
|
import com.vitorpamplona.amethyst.service.model.ContactListEvent
|
||||||
import com.vitorpamplona.amethyst.service.model.Event
|
import com.vitorpamplona.amethyst.service.model.Event
|
||||||
import com.vitorpamplona.amethyst.service.model.Event.Companion.getRefinedEvent
|
import com.vitorpamplona.amethyst.service.model.Event.Companion.getRefinedEvent
|
||||||
@@ -46,6 +47,7 @@ private object PrefKeys {
|
|||||||
const val LANGUAGE_PREFS = "languagePreferences"
|
const val LANGUAGE_PREFS = "languagePreferences"
|
||||||
const val TRANSLATE_TO = "translateTo"
|
const val TRANSLATE_TO = "translateTo"
|
||||||
const val ZAP_AMOUNTS = "zapAmounts"
|
const val ZAP_AMOUNTS = "zapAmounts"
|
||||||
|
const val ZAP_PAYMENT_REQUEST_SERVER = "zapPaymentServer"
|
||||||
const val LATEST_CONTACT_LIST = "latestContactList"
|
const val LATEST_CONTACT_LIST = "latestContactList"
|
||||||
const val HIDE_DELETE_REQUEST_DIALOG = "hide_delete_request_dialog"
|
const val HIDE_DELETE_REQUEST_DIALOG = "hide_delete_request_dialog"
|
||||||
const val HIDE_BLOCK_ALERT_DIALOG = "hide_block_alert_dialog"
|
const val HIDE_BLOCK_ALERT_DIALOG = "hide_block_alert_dialog"
|
||||||
@@ -183,6 +185,7 @@ object LocalPreferences {
|
|||||||
putString(PrefKeys.LANGUAGE_PREFS, gson.toJson(account.languagePreferences))
|
putString(PrefKeys.LANGUAGE_PREFS, gson.toJson(account.languagePreferences))
|
||||||
putString(PrefKeys.TRANSLATE_TO, account.translateTo)
|
putString(PrefKeys.TRANSLATE_TO, account.translateTo)
|
||||||
putString(PrefKeys.ZAP_AMOUNTS, gson.toJson(account.zapAmountChoices))
|
putString(PrefKeys.ZAP_AMOUNTS, gson.toJson(account.zapAmountChoices))
|
||||||
|
putString(PrefKeys.ZAP_PAYMENT_REQUEST_SERVER, gson.toJson(account.zapPaymentRequest))
|
||||||
putString(PrefKeys.LATEST_CONTACT_LIST, Event.gson.toJson(account.backupContactList))
|
putString(PrefKeys.LATEST_CONTACT_LIST, Event.gson.toJson(account.backupContactList))
|
||||||
putBoolean(PrefKeys.HIDE_DELETE_REQUEST_DIALOG, account.hideDeleteRequestDialog)
|
putBoolean(PrefKeys.HIDE_DELETE_REQUEST_DIALOG, account.hideDeleteRequestDialog)
|
||||||
putBoolean(PrefKeys.HIDE_BLOCK_ALERT_DIALOG, account.hideBlockAlertDialog)
|
putBoolean(PrefKeys.HIDE_BLOCK_ALERT_DIALOG, account.hideBlockAlertDialog)
|
||||||
@@ -210,6 +213,15 @@ object LocalPreferences {
|
|||||||
object : TypeToken<List<Long>>() {}.type
|
object : TypeToken<List<Long>>() {}.type
|
||||||
) ?: listOf(500L, 1000L, 5000L)
|
) ?: listOf(500L, 1000L, 5000L)
|
||||||
|
|
||||||
|
val zapPaymentRequestServer = try {
|
||||||
|
getString(PrefKeys.ZAP_PAYMENT_REQUEST_SERVER, null)?.let {
|
||||||
|
gson.fromJson(it, Contact::class.java)
|
||||||
|
}
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
e.printStackTrace()
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
val latestContactList = try {
|
val latestContactList = try {
|
||||||
getString(PrefKeys.LATEST_CONTACT_LIST, null)?.let {
|
getString(PrefKeys.LATEST_CONTACT_LIST, null)?.let {
|
||||||
Event.gson.fromJson(it, Event::class.java)
|
Event.gson.fromJson(it, Event::class.java)
|
||||||
@@ -244,6 +256,7 @@ object LocalPreferences {
|
|||||||
languagePreferences,
|
languagePreferences,
|
||||||
translateTo,
|
translateTo,
|
||||||
zapAmountChoices,
|
zapAmountChoices,
|
||||||
|
zapPaymentRequestServer,
|
||||||
hideDeleteRequestDialog,
|
hideDeleteRequestDialog,
|
||||||
hideBlockAlertDialog,
|
hideBlockAlertDialog,
|
||||||
latestContactList
|
latestContactList
|
||||||
|
@@ -11,6 +11,7 @@ import com.vitorpamplona.amethyst.service.model.Contact
|
|||||||
import com.vitorpamplona.amethyst.service.model.ContactListEvent
|
import com.vitorpamplona.amethyst.service.model.ContactListEvent
|
||||||
import com.vitorpamplona.amethyst.service.model.DeletionEvent
|
import com.vitorpamplona.amethyst.service.model.DeletionEvent
|
||||||
import com.vitorpamplona.amethyst.service.model.IdentityClaim
|
import com.vitorpamplona.amethyst.service.model.IdentityClaim
|
||||||
|
import com.vitorpamplona.amethyst.service.model.LnZapPaymentRequestEvent
|
||||||
import com.vitorpamplona.amethyst.service.model.LnZapRequestEvent
|
import com.vitorpamplona.amethyst.service.model.LnZapRequestEvent
|
||||||
import com.vitorpamplona.amethyst.service.model.MetadataEvent
|
import com.vitorpamplona.amethyst.service.model.MetadataEvent
|
||||||
import com.vitorpamplona.amethyst.service.model.PrivateDmEvent
|
import com.vitorpamplona.amethyst.service.model.PrivateDmEvent
|
||||||
@@ -60,6 +61,7 @@ class Account(
|
|||||||
var languagePreferences: Map<String, String> = mapOf(),
|
var languagePreferences: Map<String, String> = mapOf(),
|
||||||
var translateTo: String = Locale.getDefault().language,
|
var translateTo: String = Locale.getDefault().language,
|
||||||
var zapAmountChoices: List<Long> = listOf(500L, 1000L, 5000L),
|
var zapAmountChoices: List<Long> = listOf(500L, 1000L, 5000L),
|
||||||
|
var zapPaymentRequest: Contact? = null,
|
||||||
var hideDeleteRequestDialog: Boolean = false,
|
var hideDeleteRequestDialog: Boolean = false,
|
||||||
var hideBlockAlertDialog: Boolean = false,
|
var hideBlockAlertDialog: Boolean = false,
|
||||||
var backupContactList: ContactListEvent? = null
|
var backupContactList: ContactListEvent? = null
|
||||||
@@ -164,6 +166,20 @@ class Account(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun hasWalletConnectSetup(): Boolean {
|
||||||
|
return zapPaymentRequest != null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sendZapPaymentRequestFor(lnInvoice: String) {
|
||||||
|
if (!isWriteable()) return
|
||||||
|
|
||||||
|
zapPaymentRequest?.let {
|
||||||
|
val event = LnZapPaymentRequestEvent.create(lnInvoice, it.pubKeyHex, loggedIn.privKey!!)
|
||||||
|
|
||||||
|
Client.send(event, it.relayUri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun createZapRequestFor(user: User): LnZapRequestEvent? {
|
fun createZapRequestFor(user: User): LnZapRequestEvent? {
|
||||||
return createZapRequestFor(user.pubkeyHex)
|
return createZapRequestFor(user.pubkeyHex)
|
||||||
}
|
}
|
||||||
@@ -559,6 +575,12 @@ class Account(
|
|||||||
saveable.invalidateData()
|
saveable.invalidateData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun changeZapPaymentRequest(newServer: Contact?) {
|
||||||
|
zapPaymentRequest = newServer
|
||||||
|
live.invalidateData()
|
||||||
|
saveable.invalidateData()
|
||||||
|
}
|
||||||
|
|
||||||
fun sendChangeChannel(name: String, about: String, picture: String, channel: Channel) {
|
fun sendChangeChannel(name: String, about: String, picture: String, channel: Channel) {
|
||||||
if (!isWriteable()) return
|
if (!isWriteable()) return
|
||||||
|
|
||||||
|
@@ -0,0 +1,31 @@
|
|||||||
|
package com.vitorpamplona.amethyst.ui.note
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
import com.vitorpamplona.amethyst.model.decodePublicKey
|
||||||
|
import com.vitorpamplona.amethyst.model.toHexKey
|
||||||
|
import com.vitorpamplona.amethyst.service.model.Contact
|
||||||
|
|
||||||
|
// Rename to the corect nip number when ready.
|
||||||
|
object Nip47 {
|
||||||
|
fun parse(uri: String): Contact? {
|
||||||
|
// nostrwalletconnect://b889ff5b1513b641e2a139f661a661364979c5beee91842f8f0ef42ab558e9d4?relay=wss%3A%2F%2Frelay.damus.io&metadata=%7B%22name%22%3A%22Example%22%7D
|
||||||
|
|
||||||
|
val url = Uri.parse(uri)
|
||||||
|
|
||||||
|
if (url.scheme != "nostrwalletconnect") {
|
||||||
|
throw IllegalArgumentException("Not a Wallet Connect QR Code")
|
||||||
|
}
|
||||||
|
|
||||||
|
val pubkey = url.host ?: throw IllegalArgumentException("Hostname cannot be null")
|
||||||
|
|
||||||
|
val pubkeyHex = try {
|
||||||
|
decodePublicKey(pubkey).toHexKey()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
throw IllegalArgumentException("Hostname is not a valid Nostr Pubkey")
|
||||||
|
}
|
||||||
|
|
||||||
|
val relay = url.getQueryParameter("relay")
|
||||||
|
|
||||||
|
return Contact(pubkeyHex, relay)
|
||||||
|
}
|
||||||
|
}
|
@@ -193,6 +193,7 @@ open class Event(
|
|||||||
DeletionEvent.kind -> DeletionEvent(id, pubKey, createdAt, tags, content, sig)
|
DeletionEvent.kind -> DeletionEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
|
|
||||||
LnZapEvent.kind -> LnZapEvent(id, pubKey, createdAt, tags, content, sig)
|
LnZapEvent.kind -> LnZapEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
|
LnZapPaymentRequestEvent.kind -> LnZapPaymentRequestEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
LnZapRequestEvent.kind -> LnZapRequestEvent(id, pubKey, createdAt, tags, content, sig)
|
LnZapRequestEvent.kind -> LnZapRequestEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
LongTextNoteEvent.kind -> LongTextNoteEvent(id, pubKey, createdAt, tags, content, sig)
|
LongTextNoteEvent.kind -> LongTextNoteEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
MetadataEvent.kind -> MetadataEvent(id, pubKey, createdAt, tags, content, sig)
|
MetadataEvent.kind -> MetadataEvent(id, pubKey, createdAt, tags, content, sig)
|
||||||
|
@@ -0,0 +1,55 @@
|
|||||||
|
package com.vitorpamplona.amethyst.service.model
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import com.vitorpamplona.amethyst.model.HexKey
|
||||||
|
import com.vitorpamplona.amethyst.model.toByteArray
|
||||||
|
import com.vitorpamplona.amethyst.model.toHexKey
|
||||||
|
import nostr.postr.Utils
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
|
class LnZapPaymentRequestEvent(
|
||||||
|
id: HexKey,
|
||||||
|
pubKey: HexKey,
|
||||||
|
createdAt: Long,
|
||||||
|
tags: List<List<String>>,
|
||||||
|
content: String,
|
||||||
|
sig: HexKey
|
||||||
|
) : Event(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||||
|
|
||||||
|
fun lnInvoice(privKey: ByteArray): String? {
|
||||||
|
return try {
|
||||||
|
val sharedSecret = Utils.getSharedSecret(privKey, pubKey.toByteArray())
|
||||||
|
|
||||||
|
return Utils.decrypt(content, sharedSecret)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.w("BookmarkList", "Error decrypting the message ${e.message}")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val kind = 23194
|
||||||
|
|
||||||
|
fun create(
|
||||||
|
lnInvoice: String,
|
||||||
|
walletServicePubkey: String,
|
||||||
|
privateKey: ByteArray,
|
||||||
|
createdAt: Long = Date().time / 1000
|
||||||
|
): LnZapPaymentRequestEvent {
|
||||||
|
val pubKey = Utils.pubkeyCreate(privateKey)
|
||||||
|
|
||||||
|
val content = Utils.encrypt(
|
||||||
|
lnInvoice,
|
||||||
|
privateKey,
|
||||||
|
walletServicePubkey.toByteArray()
|
||||||
|
)
|
||||||
|
|
||||||
|
val tags = mutableListOf<List<String>>()
|
||||||
|
tags.add(listOf("p", walletServicePubkey))
|
||||||
|
|
||||||
|
val id = generateId(pubKey.toHexKey(), createdAt, kind, tags, content)
|
||||||
|
val sig = Utils.sign(id, privateKey)
|
||||||
|
return LnZapPaymentRequestEvent(id.toHexKey(), pubKey.toHexKey(), createdAt, tags, content, sig.toHexKey())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -64,8 +64,24 @@ object Client : RelayPool.Listener {
|
|||||||
RelayPool.sendFilterOnlyIfDisconnected()
|
RelayPool.sendFilterOnlyIfDisconnected()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun send(signedEvent: EventInterface) {
|
fun send(signedEvent: EventInterface, relay: String? = null) {
|
||||||
RelayPool.send(signedEvent)
|
if (relay == null) {
|
||||||
|
RelayPool.send(signedEvent)
|
||||||
|
} else {
|
||||||
|
val useConnectedRelay = relays.filter { it.url == relay }
|
||||||
|
|
||||||
|
if (useConnectedRelay.isNotEmpty()) {
|
||||||
|
useConnectedRelay.forEach {
|
||||||
|
it.send(signedEvent)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/** temporary connection */
|
||||||
|
Relay(relay, false, true, emptySet()).requestAndWatch() {
|
||||||
|
it.send(signedEvent)
|
||||||
|
it.disconnect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun close(subscriptionId: String) {
|
fun close(subscriptionId: String) {
|
||||||
|
@@ -53,6 +53,16 @@ class Relay(
|
|||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun requestAndWatch() {
|
fun requestAndWatch() {
|
||||||
|
requestAndWatch {
|
||||||
|
// Sends everything.
|
||||||
|
Client.allSubscriptions().forEach {
|
||||||
|
sendFilter(requestId = it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun requestAndWatch(onConnected: (Relay) -> Unit) {
|
||||||
if (socket != null) return
|
if (socket != null) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -65,12 +75,9 @@ class Relay(
|
|||||||
override fun onOpen(webSocket: WebSocket, response: Response) {
|
override fun onOpen(webSocket: WebSocket, response: Response) {
|
||||||
isReady = true
|
isReady = true
|
||||||
ping = response.receivedResponseAtMillis - response.sentRequestAtMillis
|
ping = response.receivedResponseAtMillis - response.sentRequestAtMillis
|
||||||
|
|
||||||
// Log.w("Relay", "Relay OnOpen, Loading All subscriptions $url")
|
// Log.w("Relay", "Relay OnOpen, Loading All subscriptions $url")
|
||||||
// Sends everything.
|
onConnected(this@Relay)
|
||||||
Client.allSubscriptions().forEach {
|
|
||||||
sendFilter(requestId = it)
|
|
||||||
}
|
|
||||||
listeners.forEach { it.onRelayStateChange(this@Relay, Type.CONNECT, null) }
|
listeners.forEach { it.onRelayStateChange(this@Relay, Type.CONNECT, null) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,29 +1,23 @@
|
|||||||
package com.vitorpamplona.amethyst.ui.note
|
package com.vitorpamplona.amethyst.ui.note
|
||||||
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.compose.animation.animateContentSize
|
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||||
import androidx.compose.foundation.layout.FlowRow
|
import androidx.compose.foundation.layout.FlowRow
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
|
||||||
import androidx.compose.material.Button
|
import androidx.compose.material.Button
|
||||||
import androidx.compose.material.ButtonDefaults
|
import androidx.compose.material.ButtonDefaults
|
||||||
import androidx.compose.material.Icon
|
import androidx.compose.material.Icon
|
||||||
import androidx.compose.material.IconButton
|
import androidx.compose.material.IconButton
|
||||||
import androidx.compose.material.MaterialTheme
|
import androidx.compose.material.MaterialTheme
|
||||||
import androidx.compose.material.OutlinedTextField
|
|
||||||
import androidx.compose.material.Surface
|
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Bolt
|
import androidx.compose.material.icons.filled.Bolt
|
||||||
@@ -47,27 +41,17 @@ import androidx.compose.ui.platform.LocalUriHandler
|
|||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.semantics.Role
|
import androidx.compose.ui.semantics.Role
|
||||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
|
||||||
import androidx.compose.ui.text.input.TextFieldValue
|
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.IntOffset
|
import androidx.compose.ui.unit.IntOffset
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.compose.ui.window.Dialog
|
|
||||||
import androidx.compose.ui.window.DialogProperties
|
|
||||||
import androidx.compose.ui.window.Popup
|
import androidx.compose.ui.window.Popup
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
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.R
|
import com.vitorpamplona.amethyst.R
|
||||||
import com.vitorpamplona.amethyst.model.Account
|
|
||||||
import com.vitorpamplona.amethyst.model.Note
|
import com.vitorpamplona.amethyst.model.Note
|
||||||
import com.vitorpamplona.amethyst.ui.actions.CloseButton
|
|
||||||
import com.vitorpamplona.amethyst.ui.actions.NewPostView
|
import com.vitorpamplona.amethyst.ui.actions.NewPostView
|
||||||
import com.vitorpamplona.amethyst.ui.actions.SaveButton
|
|
||||||
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
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -546,153 +530,6 @@ fun ZapAmountChoicePopup(baseNote: Note, accountViewModel: AccountViewModel, onD
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class UpdateZapAmountViewModel : ViewModel() {
|
|
||||||
private var account: Account? = null
|
|
||||||
|
|
||||||
var nextAmount by mutableStateOf(TextFieldValue(""))
|
|
||||||
var amountSet by mutableStateOf(listOf<Long>())
|
|
||||||
|
|
||||||
fun load(account: Account) {
|
|
||||||
this.account = account
|
|
||||||
this.amountSet = account.zapAmountChoices
|
|
||||||
}
|
|
||||||
|
|
||||||
fun toListOfAmounts(commaSeparatedAmounts: String): List<Long> {
|
|
||||||
return commaSeparatedAmounts.split(",").map { it.trim().toLongOrNull() ?: 0 }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addAmount() {
|
|
||||||
val newValue = nextAmount.text.trim().toLongOrNull()
|
|
||||||
if (newValue != null) {
|
|
||||||
amountSet = amountSet + newValue
|
|
||||||
}
|
|
||||||
|
|
||||||
nextAmount = TextFieldValue("")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun removeAmount(amount: Long) {
|
|
||||||
amountSet = amountSet - amount
|
|
||||||
}
|
|
||||||
|
|
||||||
fun sendPost() {
|
|
||||||
account?.changeZapAmounts(amountSet)
|
|
||||||
nextAmount = TextFieldValue("")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun cancel() {
|
|
||||||
nextAmount = TextFieldValue("")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun hasChanged(): Boolean {
|
|
||||||
return amountSet != account?.zapAmountChoices
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalLayoutApi::class)
|
|
||||||
@Composable
|
|
||||||
fun UpdateZapAmountDialog(onClose: () -> Unit, account: Account) {
|
|
||||||
val postViewModel: UpdateZapAmountViewModel = viewModel()
|
|
||||||
|
|
||||||
// initialize focus reference to be able to request focus programmatically
|
|
||||||
// val keyboardController = LocalSoftwareKeyboardController.current
|
|
||||||
|
|
||||||
LaunchedEffect(account) {
|
|
||||||
postViewModel.load(account)
|
|
||||||
}
|
|
||||||
|
|
||||||
Dialog(
|
|
||||||
onDismissRequest = { onClose() },
|
|
||||||
properties = DialogProperties(
|
|
||||||
dismissOnClickOutside = false,
|
|
||||||
usePlatformDefaultWidth = false
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
Surface() {
|
|
||||||
Column(modifier = Modifier.padding(10.dp)) {
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
CloseButton(onCancel = {
|
|
||||||
postViewModel.cancel()
|
|
||||||
onClose()
|
|
||||||
})
|
|
||||||
|
|
||||||
SaveButton(
|
|
||||||
onPost = {
|
|
||||||
postViewModel.sendPost()
|
|
||||||
onClose()
|
|
||||||
},
|
|
||||||
isActive = postViewModel.hasChanged()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(10.dp))
|
|
||||||
|
|
||||||
Row(modifier = Modifier.fillMaxWidth()) {
|
|
||||||
Column(modifier = Modifier.animateContentSize()) {
|
|
||||||
FlowRow(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
|
|
||||||
postViewModel.amountSet.forEach { amountInSats ->
|
|
||||||
Button(
|
|
||||||
modifier = Modifier.padding(horizontal = 3.dp),
|
|
||||||
shape = RoundedCornerShape(20.dp),
|
|
||||||
colors = ButtonDefaults
|
|
||||||
.buttonColors(
|
|
||||||
backgroundColor = MaterialTheme.colors.primary
|
|
||||||
),
|
|
||||||
onClick = {
|
|
||||||
postViewModel.removeAmount(amountInSats)
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
Text("⚡ ${showAmount(amountInSats.toBigDecimal().setScale(1))} ✖", color = Color.White, textAlign = TextAlign.Center)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(10.dp))
|
|
||||||
|
|
||||||
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
|
|
||||||
OutlinedTextField(
|
|
||||||
label = { Text(text = stringResource(R.string.new_amount_in_sats)) },
|
|
||||||
value = postViewModel.nextAmount,
|
|
||||||
onValueChange = {
|
|
||||||
postViewModel.nextAmount = it
|
|
||||||
},
|
|
||||||
keyboardOptions = KeyboardOptions.Default.copy(
|
|
||||||
capitalization = KeyboardCapitalization.None,
|
|
||||||
keyboardType = KeyboardType.Number
|
|
||||||
),
|
|
||||||
placeholder = {
|
|
||||||
Text(
|
|
||||||
text = "100, 1000, 5000",
|
|
||||||
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
singleLine = true,
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(end = 10.dp)
|
|
||||||
.weight(1f)
|
|
||||||
)
|
|
||||||
|
|
||||||
Button(
|
|
||||||
onClick = { postViewModel.addAmount() },
|
|
||||||
shape = RoundedCornerShape(20.dp),
|
|
||||||
colors = ButtonDefaults
|
|
||||||
.buttonColors(
|
|
||||||
backgroundColor = MaterialTheme.colors.primary
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
Text(text = stringResource(R.string.add), color = Color.White)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun showCount(count: Int?): String {
|
fun showCount(count: Int?): String {
|
||||||
if (count == null) return ""
|
if (count == null) return ""
|
||||||
if (count == 0) return ""
|
if (count == 0) return ""
|
||||||
|
@@ -0,0 +1,318 @@
|
|||||||
|
package com.vitorpamplona.amethyst.ui.note
|
||||||
|
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.compose.animation.animateContentSize
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||||
|
import androidx.compose.foundation.layout.FlowRow
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.material.Button
|
||||||
|
import androidx.compose.material.ButtonDefaults
|
||||||
|
import androidx.compose.material.Divider
|
||||||
|
import androidx.compose.material.Icon
|
||||||
|
import androidx.compose.material.IconButton
|
||||||
|
import androidx.compose.material.MaterialTheme
|
||||||
|
import androidx.compose.material.OutlinedTextField
|
||||||
|
import androidx.compose.material.Surface
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||||
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
|
import androidx.compose.ui.text.input.TextFieldValue
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.window.Dialog
|
||||||
|
import androidx.compose.ui.window.DialogProperties
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import com.vitorpamplona.amethyst.R
|
||||||
|
import com.vitorpamplona.amethyst.model.Account
|
||||||
|
import com.vitorpamplona.amethyst.service.model.Contact
|
||||||
|
import com.vitorpamplona.amethyst.ui.actions.CloseButton
|
||||||
|
import com.vitorpamplona.amethyst.ui.actions.SaveButton
|
||||||
|
import com.vitorpamplona.amethyst.ui.qrcode.SimpleQrCodeScanner
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class UpdateZapAmountViewModel : ViewModel() {
|
||||||
|
private var account: Account? = null
|
||||||
|
|
||||||
|
var nextAmount by mutableStateOf(TextFieldValue(""))
|
||||||
|
var amountSet by mutableStateOf(listOf<Long>())
|
||||||
|
var walletConnectRelay by mutableStateOf(TextFieldValue(""))
|
||||||
|
var walletConnectPubkey by mutableStateOf(TextFieldValue(""))
|
||||||
|
|
||||||
|
fun load(account: Account) {
|
||||||
|
this.account = account
|
||||||
|
this.amountSet = account.zapAmountChoices
|
||||||
|
this.walletConnectPubkey = account.zapPaymentRequest?.pubKeyHex?.let { TextFieldValue(it) } ?: TextFieldValue("")
|
||||||
|
this.walletConnectRelay = account.zapPaymentRequest?.relayUri?.let { TextFieldValue(it) } ?: TextFieldValue("")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toListOfAmounts(commaSeparatedAmounts: String): List<Long> {
|
||||||
|
return commaSeparatedAmounts.split(",").map { it.trim().toLongOrNull() ?: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addAmount() {
|
||||||
|
val newValue = nextAmount.text.trim().toLongOrNull()
|
||||||
|
if (newValue != null) {
|
||||||
|
amountSet = amountSet + newValue
|
||||||
|
}
|
||||||
|
|
||||||
|
nextAmount = TextFieldValue("")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeAmount(amount: Long) {
|
||||||
|
amountSet = amountSet - amount
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sendPost() {
|
||||||
|
account?.changeZapAmounts(amountSet)
|
||||||
|
|
||||||
|
if (walletConnectRelay.text.isNotBlank() && walletConnectPubkey.text.isNotBlank()) {
|
||||||
|
account?.changeZapPaymentRequest(Contact(walletConnectPubkey.text, walletConnectRelay.text))
|
||||||
|
} else {
|
||||||
|
account?.changeZapPaymentRequest(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
nextAmount = TextFieldValue("")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cancel() {
|
||||||
|
nextAmount = TextFieldValue("")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasChanged(): Boolean {
|
||||||
|
return (
|
||||||
|
amountSet != account?.zapAmountChoices ||
|
||||||
|
walletConnectPubkey.text != (account?.zapPaymentRequest?.pubKeyHex ?: "") ||
|
||||||
|
walletConnectRelay.text != (account?.zapPaymentRequest?.relayUri ?: "")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalLayoutApi::class)
|
||||||
|
@Composable
|
||||||
|
fun UpdateZapAmountDialog(onClose: () -> Unit, account: Account) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val postViewModel: UpdateZapAmountViewModel = viewModel()
|
||||||
|
|
||||||
|
LaunchedEffect(account) {
|
||||||
|
postViewModel.load(account)
|
||||||
|
}
|
||||||
|
|
||||||
|
Dialog(
|
||||||
|
onDismissRequest = { onClose() },
|
||||||
|
properties = DialogProperties(
|
||||||
|
dismissOnClickOutside = false,
|
||||||
|
usePlatformDefaultWidth = false
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Surface() {
|
||||||
|
Column(modifier = Modifier.padding(10.dp)) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
CloseButton(onCancel = {
|
||||||
|
postViewModel.cancel()
|
||||||
|
onClose()
|
||||||
|
})
|
||||||
|
|
||||||
|
SaveButton(
|
||||||
|
onPost = {
|
||||||
|
postViewModel.sendPost()
|
||||||
|
onClose()
|
||||||
|
},
|
||||||
|
isActive = postViewModel.hasChanged()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(10.dp))
|
||||||
|
|
||||||
|
Row(modifier = Modifier.fillMaxWidth()) {
|
||||||
|
Column(modifier = Modifier.animateContentSize()) {
|
||||||
|
FlowRow(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
postViewModel.amountSet.forEach { amountInSats ->
|
||||||
|
Button(
|
||||||
|
modifier = Modifier.padding(horizontal = 3.dp),
|
||||||
|
shape = RoundedCornerShape(20.dp),
|
||||||
|
colors = ButtonDefaults.buttonColors(
|
||||||
|
backgroundColor = MaterialTheme.colors.primary
|
||||||
|
),
|
||||||
|
onClick = {
|
||||||
|
postViewModel.removeAmount(amountInSats)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
"⚡ ${showAmount(amountInSats.toBigDecimal().setScale(1))} ✖",
|
||||||
|
color = Color.White,
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(10.dp))
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 5.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
OutlinedTextField(
|
||||||
|
label = { Text(text = stringResource(R.string.new_amount_in_sats)) },
|
||||||
|
value = postViewModel.nextAmount,
|
||||||
|
onValueChange = {
|
||||||
|
postViewModel.nextAmount = it
|
||||||
|
},
|
||||||
|
keyboardOptions = KeyboardOptions.Default.copy(
|
||||||
|
capitalization = KeyboardCapitalization.None,
|
||||||
|
keyboardType = KeyboardType.Number
|
||||||
|
),
|
||||||
|
placeholder = {
|
||||||
|
Text(
|
||||||
|
text = "100, 1000, 5000",
|
||||||
|
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
singleLine = true,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(end = 10.dp)
|
||||||
|
.weight(1f)
|
||||||
|
)
|
||||||
|
|
||||||
|
Button(
|
||||||
|
onClick = { postViewModel.addAmount() },
|
||||||
|
shape = RoundedCornerShape(20.dp),
|
||||||
|
colors = ButtonDefaults.buttonColors(
|
||||||
|
backgroundColor = MaterialTheme.colors.primary
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Text(text = stringResource(R.string.add), color = Color.White)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Divider(
|
||||||
|
modifier = Modifier.padding(vertical = 10.dp),
|
||||||
|
thickness = 0.25.dp
|
||||||
|
)
|
||||||
|
|
||||||
|
var qrScanning by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 5.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Text(stringResource(id = R.string.wallet_connect_service), Modifier.weight(1f))
|
||||||
|
IconButton(onClick = {
|
||||||
|
qrScanning = true
|
||||||
|
}) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(R.drawable.ic_qrcode),
|
||||||
|
null,
|
||||||
|
modifier = Modifier.size(24.dp),
|
||||||
|
tint = MaterialTheme.colors.primary
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qrScanning) {
|
||||||
|
SimpleQrCodeScanner {
|
||||||
|
qrScanning = false
|
||||||
|
if (!it.isNullOrEmpty()) {
|
||||||
|
try {
|
||||||
|
val contact = Nip47.parse(it)
|
||||||
|
if (contact != null) {
|
||||||
|
postViewModel.walletConnectPubkey = TextFieldValue(contact.pubKeyHex)
|
||||||
|
postViewModel.walletConnectRelay = TextFieldValue(contact.relayUri ?: "")
|
||||||
|
}
|
||||||
|
} catch (e: IllegalArgumentException) {
|
||||||
|
scope.launch {
|
||||||
|
Toast.makeText(context, e.message, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 5.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
OutlinedTextField(
|
||||||
|
label = { Text(text = stringResource(R.string.wallet_connect_service_pubkey)) },
|
||||||
|
value = postViewModel.walletConnectPubkey,
|
||||||
|
onValueChange = {
|
||||||
|
postViewModel.walletConnectPubkey = it
|
||||||
|
},
|
||||||
|
keyboardOptions = KeyboardOptions.Default.copy(
|
||||||
|
capitalization = KeyboardCapitalization.None
|
||||||
|
),
|
||||||
|
placeholder = {
|
||||||
|
Text(
|
||||||
|
text = "npub, hex",
|
||||||
|
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
singleLine = true,
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 5.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
OutlinedTextField(
|
||||||
|
label = { Text(text = stringResource(R.string.wallet_connect_service_relay)) },
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
value = postViewModel.walletConnectRelay,
|
||||||
|
onValueChange = { postViewModel.walletConnectRelay = it },
|
||||||
|
placeholder = {
|
||||||
|
Text(
|
||||||
|
text = "relay.server.com",
|
||||||
|
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f),
|
||||||
|
maxLines = 1
|
||||||
|
)
|
||||||
|
},
|
||||||
|
singleLine = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -12,10 +12,8 @@ import com.vitorpamplona.amethyst.R
|
|||||||
import com.vitorpamplona.amethyst.service.nip19.Nip19
|
import com.vitorpamplona.amethyst.service.nip19.Nip19
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun QrCodeScanner(onScan: (String?) -> Unit) {
|
fun NIP19QrCodeScanner(onScan: (String?) -> Unit) {
|
||||||
val lifecycleOwner = LocalLifecycleOwner.current
|
SimpleQrCodeScanner {
|
||||||
|
|
||||||
val parseQrResult = { it: String ->
|
|
||||||
try {
|
try {
|
||||||
val nip19 = Nip19.uriToRoute(it)
|
val nip19 = Nip19.uriToRoute(it)
|
||||||
val startingPage = when (nip19?.type) {
|
val startingPage = when (nip19?.type) {
|
||||||
@@ -34,11 +32,16 @@ fun QrCodeScanner(onScan: (String?) -> Unit) {
|
|||||||
onScan(null)
|
onScan(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SimpleQrCodeScanner(onScan: (String?) -> Unit) {
|
||||||
|
val lifecycleOwner = LocalLifecycleOwner.current
|
||||||
|
|
||||||
val qrLauncher =
|
val qrLauncher =
|
||||||
rememberLauncherForActivityResult(ScanContract()) {
|
rememberLauncherForActivityResult(ScanContract()) {
|
||||||
if (it.contents != null) {
|
if (it.contents != null) {
|
||||||
parseQrResult(it.contents)
|
onScan(it.contents)
|
||||||
} else {
|
} else {
|
||||||
onScan(null)
|
onScan(null)
|
||||||
}
|
}
|
||||||
|
@@ -37,7 +37,7 @@ import com.vitorpamplona.amethyst.model.User
|
|||||||
import com.vitorpamplona.amethyst.ui.actions.CloseButton
|
import com.vitorpamplona.amethyst.ui.actions.CloseButton
|
||||||
import com.vitorpamplona.amethyst.ui.components.ResizeImage
|
import com.vitorpamplona.amethyst.ui.components.ResizeImage
|
||||||
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy
|
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy
|
||||||
import com.vitorpamplona.amethyst.ui.qrcode.QrCodeScanner
|
import com.vitorpamplona.amethyst.ui.qrcode.NIP19QrCodeScanner
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ShowQRDialog(user: User, onScan: (String) -> Unit, onClose: () -> Unit) {
|
fun ShowQRDialog(user: User, onScan: (String) -> Unit, onClose: () -> Unit) {
|
||||||
@@ -135,7 +135,7 @@ fun ShowQRDialog(user: User, onScan: (String) -> Unit, onClose: () -> Unit) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
QrCodeScanner {
|
NIP19QrCodeScanner {
|
||||||
if (it.isNullOrEmpty()) {
|
if (it.isNullOrEmpty()) {
|
||||||
presenting = true
|
presenting = true
|
||||||
} else {
|
} else {
|
||||||
|
@@ -64,9 +64,13 @@ class AccountViewModel(private val account: Account) : ViewModel() {
|
|||||||
message,
|
message,
|
||||||
zapRequest?.toJson(),
|
zapRequest?.toJson(),
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
runCatching {
|
if (account.hasWalletConnectSetup()) {
|
||||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("lightning:$it"))
|
account.sendZapPaymentRequestFor(it)
|
||||||
ContextCompat.startActivity(context, intent, null)
|
} else {
|
||||||
|
runCatching {
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("lightning:$it"))
|
||||||
|
ContextCompat.startActivity(context, intent, null)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError = onError
|
onError = onError
|
||||||
|
@@ -251,10 +251,12 @@
|
|||||||
<string name="bookmarks">Bookmarks</string>
|
<string name="bookmarks">Bookmarks</string>
|
||||||
<string name="private_bookmarks">Private Bookmarks</string>
|
<string name="private_bookmarks">Private Bookmarks</string>
|
||||||
<string name="public_bookmarks">Public Bookmarks</string>
|
<string name="public_bookmarks">Public Bookmarks</string>
|
||||||
|
|
||||||
<string name="add_to_private_bookmarks">Add to Private Bookmarks</string>
|
<string name="add_to_private_bookmarks">Add to Private Bookmarks</string>
|
||||||
<string name="add_to_public_bookmarks">Add to Public Bookmarks</string>
|
<string name="add_to_public_bookmarks">Add to Public Bookmarks</string>
|
||||||
|
|
||||||
<string name="remove_from_private_bookmarks">Remove from Private Bookmarks</string>
|
<string name="remove_from_private_bookmarks">Remove from Private Bookmarks</string>
|
||||||
<string name="remove_from_public_bookmarks">Remove from Public Bookmarks</string>
|
<string name="remove_from_public_bookmarks">Remove from Public Bookmarks</string>
|
||||||
|
|
||||||
|
<string name="wallet_connect_service">Wallet Connect Service</string>
|
||||||
|
<string name="wallet_connect_service_pubkey">Wallet Connect Pubkey</string>
|
||||||
|
<string name="wallet_connect_service_relay">Wallet Connect Relay</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
Reference in New Issue
Block a user