mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2024-09-29 08:20:51 +00:00
Adds Invoice Creation to New Posts.
This commit is contained in:
parent
b0953310c2
commit
8344274011
@ -387,6 +387,10 @@ class UserMetadata {
|
||||
return listOfNotNull(name, username, display_name, displayName, nip05, lud06, lud16)
|
||||
.any { it.startsWith(prefix, true) }
|
||||
}
|
||||
|
||||
fun lnAddress(): String? {
|
||||
return (lud16?.trim() ?: lud06?.trim())?.ifBlank { null }
|
||||
}
|
||||
}
|
||||
|
||||
class UserLiveData(val user: User) : LiveData<UserState>(UserState(user)) {
|
||||
|
@ -0,0 +1,80 @@
|
||||
package com.vitorpamplona.amethyst.ui.actions
|
||||
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.model.parseDirtyWordForKey
|
||||
import com.vitorpamplona.amethyst.service.nip19.Nip19
|
||||
|
||||
class NewMessageProcessor(var originalNote: Note?, var mentions: List<User>?, var replyTos: List<Note>?, var message: String) {
|
||||
|
||||
open fun addUserToMentions(user: User) {
|
||||
mentions = if (mentions?.contains(user) == true) mentions else mentions?.plus(user) ?: listOf(user)
|
||||
}
|
||||
|
||||
open fun addNoteToReplyTos(note: Note) {
|
||||
note.author?.let { addUserToMentions(it) }
|
||||
replyTos = if (replyTos?.contains(note) == true) replyTos else replyTos?.plus(note) ?: listOf(note)
|
||||
}
|
||||
|
||||
open fun tagIndex(user: User): Int {
|
||||
// Postr Events assembles replies before mentions in the tag order
|
||||
return (if (originalNote?.channel() != null) 1 else 0) + (replyTos?.size ?: 0) + (mentions?.indexOf(user) ?: 0)
|
||||
}
|
||||
|
||||
open fun tagIndex(note: Note): Int {
|
||||
// Postr Events assembles replies before mentions in the tag order
|
||||
return (if (originalNote?.channel() != null) 1 else 0) + (replyTos?.indexOf(note) ?: 0)
|
||||
}
|
||||
|
||||
fun run() {
|
||||
// adds all references to mentions and reply tos
|
||||
message.split('\n').forEach { paragraph: String ->
|
||||
paragraph.split(' ').forEach { word: String ->
|
||||
val results = parseDirtyWordForKey(word)
|
||||
|
||||
if (results?.key?.type == Nip19.Type.USER) {
|
||||
addUserToMentions(LocalCache.getOrCreateUser(results.key.hex))
|
||||
} else if (results?.key?.type == Nip19.Type.NOTE) {
|
||||
addNoteToReplyTos(LocalCache.getOrCreateNote(results.key.hex))
|
||||
} else if (results?.key?.type == Nip19.Type.EVENT) {
|
||||
addNoteToReplyTos(LocalCache.getOrCreateNote(results.key.hex))
|
||||
} else if (results?.key?.type == Nip19.Type.ADDRESS) {
|
||||
val note = LocalCache.checkGetOrCreateAddressableNote(results.key.hex)
|
||||
if (note != null) {
|
||||
addNoteToReplyTos(note)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tags the text in the correct order.
|
||||
message = message.split('\n').map { paragraph: String ->
|
||||
paragraph.split(' ').map { word: String ->
|
||||
val results = parseDirtyWordForKey(word)
|
||||
if (results?.key?.type == Nip19.Type.USER) {
|
||||
val user = LocalCache.getOrCreateUser(results.key.hex)
|
||||
|
||||
"#[${tagIndex(user)}]${results.restOfWord}"
|
||||
} else if (results?.key?.type == Nip19.Type.NOTE) {
|
||||
val note = LocalCache.getOrCreateNote(results.key.hex)
|
||||
|
||||
"#[${tagIndex(note)}]${results.restOfWord}"
|
||||
} else if (results?.key?.type == Nip19.Type.EVENT) {
|
||||
val note = LocalCache.getOrCreateNote(results.key.hex)
|
||||
|
||||
"#[${tagIndex(note)}]${results.restOfWord}"
|
||||
} else if (results?.key?.type == Nip19.Type.ADDRESS) {
|
||||
val note = LocalCache.checkGetOrCreateAddressableNote(results.key.hex)
|
||||
if (note != null) {
|
||||
"#[${tagIndex(note)}]${results.restOfWord}"
|
||||
} else {
|
||||
word
|
||||
}
|
||||
} else {
|
||||
word
|
||||
}
|
||||
}.joinToString(" ")
|
||||
}.joinToString("\n")
|
||||
}
|
||||
}
|
@ -12,6 +12,8 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.MonetizationOn
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
@ -29,6 +31,7 @@ import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
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.TextFieldValue
|
||||
import androidx.compose.ui.text.style.TextDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
@ -182,6 +185,28 @@ fun NewPostView(onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = n
|
||||
}
|
||||
}
|
||||
|
||||
val user = postViewModel.account?.userProfile()
|
||||
val lud16 = user?.info?.lnAddress()
|
||||
|
||||
if (lud16 != null && user != null && postViewModel.wantsInvoice) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(vertical = 5.dp)) {
|
||||
InvoiceRequest(
|
||||
lud16,
|
||||
user.pubkeyHex,
|
||||
account,
|
||||
stringResource(id = R.string.lightning_invoice),
|
||||
stringResource(id = R.string.lightning_create_and_add_invoice),
|
||||
onSuccess = {
|
||||
postViewModel.message = TextFieldValue(postViewModel.message.text + "\n\n" + it)
|
||||
postViewModel.wantsInvoice = false
|
||||
},
|
||||
onClose = {
|
||||
postViewModel.wantsInvoice = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val myUrlPreview = postViewModel.urlPreview
|
||||
if (myUrlPreview != null) {
|
||||
Row(modifier = Modifier.padding(top = 5.dp)) {
|
||||
@ -249,6 +274,12 @@ fun NewPostView(onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = n
|
||||
postViewModel.wantsPoll = !postViewModel.wantsPoll
|
||||
}
|
||||
}
|
||||
|
||||
if (postViewModel.canAddLnInvoice()) {
|
||||
AddLnInvoiceButton(postViewModel.wantsInvoice) {
|
||||
postViewModel.wantsInvoice = !postViewModel.wantsInvoice
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -284,6 +315,35 @@ private fun AddPollButton(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
private fun AddLnInvoiceButton(
|
||||
isLnInvoiceActive: Boolean,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
IconButton(
|
||||
onClick = {
|
||||
onClick()
|
||||
}
|
||||
) {
|
||||
if (!isLnInvoiceActive) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.MonetizationOn,
|
||||
null,
|
||||
modifier = Modifier.size(20.dp),
|
||||
tint = MaterialTheme.colors.onBackground
|
||||
)
|
||||
} else {
|
||||
Icon(
|
||||
imageVector = Icons.Default.MonetizationOn,
|
||||
null,
|
||||
modifier = Modifier.size(20.dp),
|
||||
tint = Color.Green.copy(alpha = 0.52f)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CloseButton(onCancel: () -> Unit) {
|
||||
Button(
|
||||
|
@ -53,6 +53,10 @@ open class NewPostViewModel : ViewModel() {
|
||||
var isValidConsensusThreshold = mutableStateOf(true)
|
||||
var isValidClosedAt = mutableStateOf(true)
|
||||
|
||||
// Invoices
|
||||
var wantsInvoice by mutableStateOf(false)
|
||||
|
||||
|
||||
open fun load(account: Account, replyingTo: Note?, quote: Note?) {
|
||||
originalNote = replyingTo
|
||||
replyingTo?.let { replyNote ->
|
||||
@ -199,6 +203,8 @@ open class NewPostViewModel : ViewModel() {
|
||||
valueMinimum = null
|
||||
consensusThreshold = null
|
||||
closedAt = null
|
||||
|
||||
wantsInvoice = false
|
||||
}
|
||||
|
||||
open fun findUrlInMessage(): String? {
|
||||
@ -248,11 +254,15 @@ open class NewPostViewModel : ViewModel() {
|
||||
}
|
||||
|
||||
fun canPost(): Boolean {
|
||||
return message.text.isNotBlank() && !isUploadingImage &&
|
||||
return message.text.isNotBlank() && !isUploadingImage && !wantsInvoice &&
|
||||
(!wantsPoll || pollOptions.values.all { it.isNotEmpty() })
|
||||
}
|
||||
|
||||
fun canUsePoll(): Boolean {
|
||||
return originalNote?.event !is PrivateDmEvent && originalNote?.channel() == null
|
||||
}
|
||||
|
||||
fun canAddLnInvoice(): Boolean {
|
||||
return account?.userProfile()?.info?.lnAddress() != null
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
package com.vitorpamplona.amethyst.ui.components
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@ -36,14 +34,21 @@ import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.core.content.ContextCompat.startActivity
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.service.lnurl.LightningAddressResolver
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun InvoiceRequest(lud16: String, toUserPubKeyHex: String, account: Account, onClose: () -> Unit) {
|
||||
fun InvoiceRequest(
|
||||
lud16: String,
|
||||
toUserPubKeyHex: String,
|
||||
account: Account,
|
||||
titleText: String? = null,
|
||||
buttonText: String? = null,
|
||||
onSuccess: (String) -> Unit,
|
||||
onClose: () -> Unit
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
@ -73,7 +78,7 @@ fun InvoiceRequest(lud16: String, toUserPubKeyHex: String, account: Account, onC
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.lightning_tips),
|
||||
text = titleText ?: stringResource(R.string.lightning_tips),
|
||||
fontSize = 20.sp,
|
||||
fontWeight = FontWeight.W500,
|
||||
modifier = Modifier.padding(start = 10.dp)
|
||||
@ -137,13 +142,7 @@ fun InvoiceRequest(lud16: String, toUserPubKeyHex: String, account: Account, onC
|
||||
amount * 1000,
|
||||
message,
|
||||
zapRequest?.toJson(),
|
||||
onSuccess = {
|
||||
runCatching {
|
||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("lightning:$it"))
|
||||
startActivity(context, intent, null)
|
||||
}
|
||||
onClose()
|
||||
},
|
||||
onSuccess = onSuccess,
|
||||
onError = {
|
||||
scope.launch {
|
||||
Toast.makeText(context, it, Toast.LENGTH_SHORT).show()
|
||||
@ -159,7 +158,7 @@ fun InvoiceRequest(lud16: String, toUserPubKeyHex: String, account: Account, onC
|
||||
backgroundColor = MaterialTheme.colors.primary
|
||||
)
|
||||
) {
|
||||
Text(text = stringResource(R.string.send_sats), color = Color.White, fontSize = 20.sp)
|
||||
Text(text = buttonText ?: stringResource(R.string.send_sats), color = Color.White, fontSize = 20.sp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.gestures.scrollBy
|
||||
import androidx.compose.foundation.layout.*
|
||||
@ -27,6 +29,7 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.layout.onSizeChanged
|
||||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
@ -39,6 +42,7 @@ import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleEventObserver
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
@ -421,6 +425,7 @@ private fun DrawAdditionalInfo(baseUser: User, account: Account, accountViewMode
|
||||
|
||||
val uri = LocalUriHandler.current
|
||||
val clipboardManager = LocalClipboardManager.current
|
||||
val context = LocalContext.current
|
||||
|
||||
Row(verticalAlignment = Alignment.Bottom) {
|
||||
user.bestDisplayName()?.let {
|
||||
@ -555,9 +560,22 @@ private fun DrawAdditionalInfo(baseUser: User, account: Account, accountViewMode
|
||||
|
||||
if (zapExpanded) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(vertical = 5.dp)) {
|
||||
InvoiceRequest(lud16, baseUser.pubkeyHex, account) {
|
||||
zapExpanded = false
|
||||
}
|
||||
InvoiceRequest(lud16, baseUser.pubkeyHex, account,
|
||||
onSuccess = {
|
||||
// pay directly
|
||||
if (account.hasWalletConnectSetup()) {
|
||||
account.sendZapPaymentRequestFor(it)
|
||||
} else {
|
||||
runCatching {
|
||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("lightning:$it"))
|
||||
ContextCompat.startActivity(context, intent, null)
|
||||
}
|
||||
}
|
||||
},
|
||||
onClose = {
|
||||
zapExpanded = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -288,4 +288,6 @@
|
||||
|
||||
<string name="custom_zaps_add_a_message">Add a public message</string>
|
||||
<string name="custom_zaps_add_a_message_example">Thank you for all your work!</string>
|
||||
|
||||
<string name="lightning_create_and_add_invoice">Create and Add</string>
|
||||
</resources>
|
||||
|
Loading…
Reference in New Issue
Block a user