Zap Forwarding
This commit is contained in:
@@ -422,7 +422,7 @@ class Account(
|
|||||||
return LocalCache.notes[signedEvent.id]
|
return LocalCache.notes[signedEvent.id]
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendPost(message: String, replyTo: List<Note>?, mentions: List<User>?, tags: List<String>? = null) {
|
fun sendPost(message: String, replyTo: List<Note>?, mentions: List<User>?, tags: List<String>? = null, zapReceiver: String? = null) {
|
||||||
if (!isWriteable()) return
|
if (!isWriteable()) return
|
||||||
|
|
||||||
val repliesToHex = replyTo?.filter { it.address() == null }?.map { it.idHex }
|
val repliesToHex = replyTo?.filter { it.address() == null }?.map { it.idHex }
|
||||||
@@ -435,6 +435,7 @@ class Account(
|
|||||||
mentions = mentionsHex,
|
mentions = mentionsHex,
|
||||||
addresses = addresses,
|
addresses = addresses,
|
||||||
extraTags = tags,
|
extraTags = tags,
|
||||||
|
zapReceiver = zapReceiver,
|
||||||
privateKey = loggedIn.privKey!!
|
privateKey = loggedIn.privKey!!
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -450,7 +451,8 @@ class Account(
|
|||||||
valueMaximum: Int?,
|
valueMaximum: Int?,
|
||||||
valueMinimum: Int?,
|
valueMinimum: Int?,
|
||||||
consensusThreshold: Int?,
|
consensusThreshold: Int?,
|
||||||
closedAt: Int?
|
closedAt: Int?,
|
||||||
|
zapReceiver: String? = null
|
||||||
) {
|
) {
|
||||||
if (!isWriteable()) return
|
if (!isWriteable()) return
|
||||||
|
|
||||||
@@ -468,14 +470,15 @@ class Account(
|
|||||||
valueMaximum = valueMaximum,
|
valueMaximum = valueMaximum,
|
||||||
valueMinimum = valueMinimum,
|
valueMinimum = valueMinimum,
|
||||||
consensusThreshold = consensusThreshold,
|
consensusThreshold = consensusThreshold,
|
||||||
closedAt = closedAt
|
closedAt = closedAt,
|
||||||
|
zapReceiver = zapReceiver
|
||||||
)
|
)
|
||||||
// println("Sending new PollNoteEvent: %s".format(signedEvent.toJson()))
|
// println("Sending new PollNoteEvent: %s".format(signedEvent.toJson()))
|
||||||
Client.send(signedEvent)
|
Client.send(signedEvent)
|
||||||
LocalCache.consume(signedEvent)
|
LocalCache.consume(signedEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendChannelMessage(message: String, toChannel: String, replyTo: List<Note>?, mentions: List<User>?) {
|
fun sendChannelMessage(message: String, toChannel: String, replyTo: List<Note>?, mentions: List<User>?, zapReceiver: String? = null) {
|
||||||
if (!isWriteable()) return
|
if (!isWriteable()) return
|
||||||
|
|
||||||
// val repliesToHex = listOfNotNull(replyingTo?.idHex).ifEmpty { null }
|
// val repliesToHex = listOfNotNull(replyingTo?.idHex).ifEmpty { null }
|
||||||
@@ -487,13 +490,14 @@ class Account(
|
|||||||
channel = toChannel,
|
channel = toChannel,
|
||||||
replyTos = repliesToHex,
|
replyTos = repliesToHex,
|
||||||
mentions = mentionsHex,
|
mentions = mentionsHex,
|
||||||
|
zapReceiver = zapReceiver,
|
||||||
privateKey = loggedIn.privKey!!
|
privateKey = loggedIn.privKey!!
|
||||||
)
|
)
|
||||||
Client.send(signedEvent)
|
Client.send(signedEvent)
|
||||||
LocalCache.consume(signedEvent, null)
|
LocalCache.consume(signedEvent, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendPrivateMessage(message: String, toUser: String, replyingTo: Note? = null, mentions: List<User>?) {
|
fun sendPrivateMessage(message: String, toUser: String, replyingTo: Note? = null, mentions: List<User>?, zapReceiver: String? = null) {
|
||||||
if (!isWriteable()) return
|
if (!isWriteable()) return
|
||||||
val user = LocalCache.users[toUser] ?: return
|
val user = LocalCache.users[toUser] ?: return
|
||||||
|
|
||||||
@@ -506,6 +510,7 @@ class Account(
|
|||||||
msg = message,
|
msg = message,
|
||||||
replyTos = repliesToHex,
|
replyTos = repliesToHex,
|
||||||
mentions = mentionsHex,
|
mentions = mentionsHex,
|
||||||
|
zapReceiver = zapReceiver,
|
||||||
privateKey = loggedIn.privKey!!,
|
privateKey = loggedIn.privKey!!,
|
||||||
advertiseNip18 = false
|
advertiseNip18 = false
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class ChannelMessageEvent(
|
|||||||
companion object {
|
companion object {
|
||||||
const val kind = 42
|
const val kind = 42
|
||||||
|
|
||||||
fun create(message: String, channel: String, replyTos: List<String>? = null, mentions: List<String>? = null, privateKey: ByteArray, createdAt: Long = Date().time / 1000): ChannelMessageEvent {
|
fun create(message: String, channel: String, replyTos: List<String>? = null, mentions: List<String>? = null, zapReceiver: String?, privateKey: ByteArray, createdAt: Long = Date().time / 1000): ChannelMessageEvent {
|
||||||
val content = message
|
val content = message
|
||||||
val pubKey = Utils.pubkeyCreate(privateKey).toHexKey()
|
val pubKey = Utils.pubkeyCreate(privateKey).toHexKey()
|
||||||
val tags = mutableListOf(
|
val tags = mutableListOf(
|
||||||
@@ -32,6 +32,9 @@ class ChannelMessageEvent(
|
|||||||
mentions?.forEach {
|
mentions?.forEach {
|
||||||
tags.add(listOf("p", it))
|
tags.add(listOf("p", it))
|
||||||
}
|
}
|
||||||
|
zapReceiver?.let {
|
||||||
|
tags.add(listOf("zap", it))
|
||||||
|
}
|
||||||
|
|
||||||
val id = generateId(pubKey, createdAt, kind, tags, content)
|
val id = generateId(pubKey, createdAt, kind, tags, content)
|
||||||
val sig = Utils.sign(id, privateKey)
|
val sig = Utils.sign(id, privateKey)
|
||||||
|
|||||||
@@ -48,7 +48,8 @@ class PollNoteEvent(
|
|||||||
valueMaximum: Int?,
|
valueMaximum: Int?,
|
||||||
valueMinimum: Int?,
|
valueMinimum: Int?,
|
||||||
consensusThreshold: Int?,
|
consensusThreshold: Int?,
|
||||||
closedAt: Int?
|
closedAt: Int?,
|
||||||
|
zapReceiver: String?
|
||||||
): PollNoteEvent {
|
): PollNoteEvent {
|
||||||
val pubKey = Utils.pubkeyCreate(privateKey).toHexKey()
|
val pubKey = Utils.pubkeyCreate(privateKey).toHexKey()
|
||||||
val tags = mutableListOf<List<String>>()
|
val tags = mutableListOf<List<String>>()
|
||||||
@@ -68,6 +69,11 @@ class PollNoteEvent(
|
|||||||
tags.add(listOf(VALUE_MINIMUM, valueMinimum.toString()))
|
tags.add(listOf(VALUE_MINIMUM, valueMinimum.toString()))
|
||||||
tags.add(listOf(CONSENSUS_THRESHOLD, consensusThreshold.toString()))
|
tags.add(listOf(CONSENSUS_THRESHOLD, consensusThreshold.toString()))
|
||||||
tags.add(listOf(CLOSED_AT, closedAt.toString()))
|
tags.add(listOf(CLOSED_AT, closedAt.toString()))
|
||||||
|
|
||||||
|
if (zapReceiver != null) {
|
||||||
|
tags.add(listOf("zap", zapReceiver))
|
||||||
|
}
|
||||||
|
|
||||||
val id = generateId(pubKey, createdAt, kind, tags, msg)
|
val id = generateId(pubKey, createdAt, kind, tags, msg)
|
||||||
val sig = Utils.sign(id, privateKey)
|
val sig = Utils.sign(id, privateKey)
|
||||||
return PollNoteEvent(id.toHexKey(), pubKey, createdAt, tags, msg, sig.toHexKey())
|
return PollNoteEvent(id.toHexKey(), pubKey, createdAt, tags, msg, sig.toHexKey())
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ class PrivateDmEvent(
|
|||||||
msg: String,
|
msg: String,
|
||||||
replyTos: List<String>? = null,
|
replyTos: List<String>? = null,
|
||||||
mentions: List<String>? = null,
|
mentions: List<String>? = null,
|
||||||
|
zapReceiver: String?,
|
||||||
privateKey: ByteArray,
|
privateKey: ByteArray,
|
||||||
createdAt: Long = Date().time / 1000,
|
createdAt: Long = Date().time / 1000,
|
||||||
publishedRecipientPubKey: ByteArray? = null,
|
publishedRecipientPubKey: ByteArray? = null,
|
||||||
@@ -79,6 +80,9 @@ class PrivateDmEvent(
|
|||||||
mentions?.forEach {
|
mentions?.forEach {
|
||||||
tags.add(listOf("p", it))
|
tags.add(listOf("p", it))
|
||||||
}
|
}
|
||||||
|
zapReceiver?.let {
|
||||||
|
tags.add(listOf("zap", it))
|
||||||
|
}
|
||||||
val id = generateId(pubKey, createdAt, kind, tags, content)
|
val id = generateId(pubKey, createdAt, kind, tags, content)
|
||||||
val sig = Utils.sign(id, privateKey)
|
val sig = Utils.sign(id, privateKey)
|
||||||
return PrivateDmEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig.toHexKey())
|
return PrivateDmEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig.toHexKey())
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ class TextNoteEvent(
|
|||||||
mentions: List<String>?,
|
mentions: List<String>?,
|
||||||
addresses: List<ATag>?,
|
addresses: List<ATag>?,
|
||||||
extraTags: List<String>?,
|
extraTags: List<String>?,
|
||||||
|
zapReceiver: String?,
|
||||||
privateKey: ByteArray,
|
privateKey: ByteArray,
|
||||||
createdAt: Long = Date().time / 1000
|
createdAt: Long = Date().time / 1000
|
||||||
): TextNoteEvent {
|
): TextNoteEvent {
|
||||||
@@ -46,9 +47,13 @@ class TextNoteEvent(
|
|||||||
extraTags?.forEach {
|
extraTags?.forEach {
|
||||||
tags.add(listOf("t", it))
|
tags.add(listOf("t", it))
|
||||||
}
|
}
|
||||||
|
zapReceiver?.let {
|
||||||
|
tags.add(listOf("zap", it))
|
||||||
|
}
|
||||||
findURLs(msg).forEach {
|
findURLs(msg).forEach {
|
||||||
tags.add(listOf("r", it))
|
tags.add(listOf("r", it))
|
||||||
}
|
}
|
||||||
|
|
||||||
val id = generateId(pubKey, createdAt, kind, tags, msg)
|
val id = generateId(pubKey, createdAt, kind, tags, msg)
|
||||||
val sig = Utils.sign(id, privateKey)
|
val sig = Utils.sign(id, privateKey)
|
||||||
return TextNoteEvent(id.toHexKey(), pubKey, createdAt, tags, msg, sig.toHexKey())
|
return TextNoteEvent(id.toHexKey(), pubKey, createdAt, tags, msg, sig.toHexKey())
|
||||||
|
|||||||
@@ -17,8 +17,12 @@ import androidx.compose.foundation.text.KeyboardOptions
|
|||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.*
|
import androidx.compose.material.*
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.ArrowForwardIos
|
||||||
|
import androidx.compose.material.icons.filled.Bolt
|
||||||
import androidx.compose.material.icons.filled.Cancel
|
import androidx.compose.material.icons.filled.Cancel
|
||||||
import androidx.compose.material.icons.filled.CurrencyBitcoin
|
import androidx.compose.material.icons.filled.CurrencyBitcoin
|
||||||
|
import androidx.compose.material.icons.outlined.ArrowForwardIos
|
||||||
|
import androidx.compose.material.icons.outlined.Bolt
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -205,17 +209,19 @@ fun NewPostView(onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = n
|
|||||||
|
|
||||||
val url = postViewModel.contentToAddUrl
|
val url = postViewModel.contentToAddUrl
|
||||||
if (url != null) {
|
if (url != null) {
|
||||||
ImageVideoDescription(
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
url,
|
ImageVideoDescription(
|
||||||
account.defaultFileServer,
|
url,
|
||||||
onAdd = { description, server ->
|
account.defaultFileServer,
|
||||||
postViewModel.upload(url, description, server, context)
|
onAdd = { description, server ->
|
||||||
account.changeDefaultFileServer(server)
|
postViewModel.upload(url, description, server, context)
|
||||||
},
|
account.changeDefaultFileServer(server)
|
||||||
onCancel = {
|
},
|
||||||
postViewModel.contentToAddUrl = null
|
onCancel = {
|
||||||
}
|
postViewModel.contentToAddUrl = null
|
||||||
)
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val user = postViewModel.account?.userProfile()
|
val user = postViewModel.account?.userProfile()
|
||||||
@@ -301,11 +307,11 @@ fun NewPostView(onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = n
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Row(modifier = Modifier.fillMaxWidth()) {
|
Row(modifier = Modifier.fillMaxWidth().height(50.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||||
UploadFromGallery(
|
UploadFromGallery(
|
||||||
isUploading = postViewModel.isUploadingImage,
|
isUploading = postViewModel.isUploadingImage,
|
||||||
tint = MaterialTheme.colors.onBackground,
|
tint = MaterialTheme.colors.onBackground,
|
||||||
modifier = Modifier.padding(bottom = 10.dp)
|
modifier = Modifier
|
||||||
) {
|
) {
|
||||||
postViewModel.selectImage(it)
|
postViewModel.selectImage(it)
|
||||||
}
|
}
|
||||||
@@ -323,6 +329,10 @@ fun NewPostView(onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = n
|
|||||||
postViewModel.wantsInvoice = !postViewModel.wantsInvoice
|
postViewModel.wantsInvoice = !postViewModel.wantsInvoice
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ForwardZapTo(postViewModel) {
|
||||||
|
postViewModel.wantsForwardZapTo = !postViewModel.wantsForwardZapTo
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -386,6 +396,71 @@ private fun AddLnInvoiceButton(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ForwardZapTo(
|
||||||
|
postViewModel: NewPostViewModel,
|
||||||
|
onClick: () -> Unit
|
||||||
|
) {
|
||||||
|
IconButton(
|
||||||
|
onClick = {
|
||||||
|
onClick()
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Box(Modifier.height(20.dp).width(25.dp)) {
|
||||||
|
if (!postViewModel.wantsForwardZapTo) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Bolt,
|
||||||
|
contentDescription = stringResource(R.string.zaps),
|
||||||
|
modifier = Modifier.size(20.dp).align(Alignment.CenterStart),
|
||||||
|
tint = MaterialTheme.colors.onBackground
|
||||||
|
)
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.ArrowForwardIos,
|
||||||
|
contentDescription = stringResource(R.string.zaps),
|
||||||
|
modifier = Modifier.size(13.dp).align(Alignment.CenterEnd),
|
||||||
|
tint = MaterialTheme.colors.onBackground
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Bolt,
|
||||||
|
contentDescription = stringResource(id = R.string.zaps),
|
||||||
|
modifier = Modifier.size(20.dp).align(Alignment.CenterStart),
|
||||||
|
tint = BitcoinOrange
|
||||||
|
)
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.ArrowForwardIos,
|
||||||
|
contentDescription = stringResource(id = R.string.zaps),
|
||||||
|
modifier = Modifier.size(13.dp).align(Alignment.CenterEnd),
|
||||||
|
tint = BitcoinOrange
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (postViewModel.wantsForwardZapTo) {
|
||||||
|
OutlinedTextField(
|
||||||
|
value = postViewModel.forwardZapToEditting,
|
||||||
|
onValueChange = {
|
||||||
|
postViewModel.updateZapForwardTo(it)
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxWidth().windowInsetsPadding(WindowInsets(0.dp, 0.dp, 0.dp, 0.dp)).padding(0.dp),
|
||||||
|
placeholder = {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.zap_forward_lnAddress),
|
||||||
|
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
colors = TextFieldDefaults
|
||||||
|
.outlinedTextFieldColors(
|
||||||
|
unfocusedBorderColor = Color.Transparent,
|
||||||
|
focusedBorderColor = Color.Transparent
|
||||||
|
),
|
||||||
|
visualTransformation = UrlUserTagTransformation(MaterialTheme.colors.primary),
|
||||||
|
textStyle = LocalTextStyle.current.copy(textDirection = TextDirection.Content)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CloseButton(onCancel: () -> Unit) {
|
fun CloseButton(onCancel: () -> Unit) {
|
||||||
Button(
|
Button(
|
||||||
@@ -555,7 +630,7 @@ fun ImageVideoDescription(
|
|||||||
fontWeight = FontWeight.W500,
|
fontWeight = FontWeight.W500,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(start = 10.dp)
|
.padding(start = 10.dp)
|
||||||
.weight(1.0f)
|
.weight(1.0f).windowInsetsPadding(WindowInsets(0.dp, 0.dp, 0.dp, 0.dp))
|
||||||
)
|
)
|
||||||
|
|
||||||
IconButton(
|
IconButton(
|
||||||
@@ -579,7 +654,7 @@ fun ImageVideoDescription(
|
|||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(bottom = 10.dp)
|
.padding(bottom = 10.dp).windowInsetsPadding(WindowInsets(0.dp, 0.dp, 0.dp, 0.dp))
|
||||||
) {
|
) {
|
||||||
if (mediaType.startsWith("image")) {
|
if (mediaType.startsWith("image")) {
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
@@ -588,7 +663,7 @@ fun ImageVideoDescription(
|
|||||||
contentScale = ContentScale.FillWidth,
|
contentScale = ContentScale.FillWidth,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(top = 4.dp)
|
.padding(top = 4.dp)
|
||||||
.fillMaxWidth()
|
.fillMaxWidth().windowInsetsPadding(WindowInsets(0.dp, 0.dp, 0.dp, 0.dp))
|
||||||
)
|
)
|
||||||
} else if (mediaType.startsWith("video") && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
} else if (mediaType.startsWith("video") && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
var bitmap by remember { mutableStateOf<Bitmap?>(null) }
|
var bitmap by remember { mutableStateOf<Bitmap?>(null) }
|
||||||
@@ -625,18 +700,18 @@ fun ImageVideoDescription(
|
|||||||
onSelect = {
|
onSelect = {
|
||||||
selectedServer = fileServers[it].first
|
selectedServer = fileServers[it].first
|
||||||
},
|
},
|
||||||
modifier = Modifier
|
modifier = Modifier.windowInsetsPadding(WindowInsets(0.dp, 0.dp, 0.dp, 0.dp))
|
||||||
.weight(1f)
|
.weight(1f)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth().windowInsetsPadding(WindowInsets(0.dp, 0.dp, 0.dp, 0.dp))
|
||||||
) {
|
) {
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
label = { Text(text = stringResource(R.string.content_description)) },
|
label = { Text(text = stringResource(R.string.content_description)) },
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth().windowInsetsPadding(WindowInsets(0.dp, 0.dp, 0.dp, 0.dp)),
|
||||||
value = message,
|
value = message,
|
||||||
onValueChange = { message = it },
|
onValueChange = { message = it },
|
||||||
placeholder = {
|
placeholder = {
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ open class NewPostViewModel : ViewModel() {
|
|||||||
|
|
||||||
var userSuggestions by mutableStateOf<List<User>>(emptyList())
|
var userSuggestions by mutableStateOf<List<User>>(emptyList())
|
||||||
var userSuggestionAnchor: TextRange? = null
|
var userSuggestionAnchor: TextRange? = null
|
||||||
|
var userSuggestionsMainMessage: Boolean? = null
|
||||||
|
|
||||||
// Images and Videos
|
// Images and Videos
|
||||||
var contentToAddUrl by mutableStateOf<Uri?>(null)
|
var contentToAddUrl by mutableStateOf<Uri?>(null)
|
||||||
@@ -61,6 +62,11 @@ open class NewPostViewModel : ViewModel() {
|
|||||||
var canAddInvoice by mutableStateOf(false)
|
var canAddInvoice by mutableStateOf(false)
|
||||||
var wantsInvoice by mutableStateOf(false)
|
var wantsInvoice by mutableStateOf(false)
|
||||||
|
|
||||||
|
// Forward Zap to
|
||||||
|
var wantsForwardZapTo by mutableStateOf(false)
|
||||||
|
var forwardZapTo by mutableStateOf<User?>(null)
|
||||||
|
var forwardZapToEditting by mutableStateOf(TextFieldValue(""))
|
||||||
|
|
||||||
open fun load(account: Account, replyingTo: Note?, quote: Note?) {
|
open fun load(account: Account, replyingTo: Note?, quote: Note?) {
|
||||||
originalNote = replyingTo
|
originalNote = replyingTo
|
||||||
replyingTo?.let { replyNote ->
|
replyingTo?.let { replyNote ->
|
||||||
@@ -90,6 +96,10 @@ open class NewPostViewModel : ViewModel() {
|
|||||||
canUsePoll = originalNote?.event !is PrivateDmEvent && originalNote?.channel() == null
|
canUsePoll = originalNote?.event !is PrivateDmEvent && originalNote?.channel() == null
|
||||||
contentToAddUrl = null
|
contentToAddUrl = null
|
||||||
|
|
||||||
|
wantsForwardZapTo = false
|
||||||
|
forwardZapTo = null
|
||||||
|
forwardZapToEditting = TextFieldValue("")
|
||||||
|
|
||||||
this.account = account
|
this.account = account
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,14 +107,24 @@ open class NewPostViewModel : ViewModel() {
|
|||||||
val tagger = NewMessageTagger(originalNote?.channel(), mentions, replyTos, message.text)
|
val tagger = NewMessageTagger(originalNote?.channel(), mentions, replyTos, message.text)
|
||||||
tagger.run()
|
tagger.run()
|
||||||
|
|
||||||
if (wantsPoll) {
|
val zapReceiver = if (wantsForwardZapTo) {
|
||||||
account?.sendPoll(tagger.message, tagger.replyTos, tagger.mentions, pollOptions, valueMaximum, valueMinimum, consensusThreshold, closedAt)
|
if (forwardZapTo != null) {
|
||||||
} else if (originalNote?.channel() != null) {
|
forwardZapTo?.info?.lud16 ?: forwardZapTo?.info?.lud06
|
||||||
account?.sendChannelMessage(tagger.message, tagger.channel!!.idHex, tagger.replyTos, tagger.mentions)
|
} else {
|
||||||
} else if (originalNote?.event is PrivateDmEvent) {
|
forwardZapToEditting.text
|
||||||
account?.sendPrivateMessage(tagger.message, originalNote!!.author!!.pubkeyHex, originalNote!!, tagger.mentions)
|
}
|
||||||
} else {
|
} else {
|
||||||
account?.sendPost(tagger.message, tagger.replyTos, tagger.mentions)
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wantsPoll) {
|
||||||
|
account?.sendPoll(tagger.message, tagger.replyTos, tagger.mentions, pollOptions, valueMaximum, valueMinimum, consensusThreshold, closedAt, zapReceiver)
|
||||||
|
} else if (originalNote?.channel() != null) {
|
||||||
|
account?.sendChannelMessage(tagger.message, tagger.channel!!.idHex, tagger.replyTos, tagger.mentions, zapReceiver)
|
||||||
|
} else if (originalNote?.event is PrivateDmEvent) {
|
||||||
|
account?.sendPrivateMessage(tagger.message, originalNote!!.author!!.pubkeyHex, originalNote!!, tagger.mentions, zapReceiver)
|
||||||
|
} else {
|
||||||
|
account?.sendPost(tagger.message, tagger.replyTos, tagger.mentions, null, zapReceiver)
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel()
|
cancel()
|
||||||
@@ -155,6 +175,14 @@ open class NewPostViewModel : ViewModel() {
|
|||||||
closedAt = null
|
closedAt = null
|
||||||
|
|
||||||
wantsInvoice = false
|
wantsInvoice = false
|
||||||
|
|
||||||
|
wantsForwardZapTo = false
|
||||||
|
forwardZapTo = null
|
||||||
|
forwardZapToEditting = TextFieldValue("")
|
||||||
|
|
||||||
|
userSuggestions = emptyList()
|
||||||
|
userSuggestionAnchor = null
|
||||||
|
userSuggestionsMainMessage = null
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun findUrlInMessage(): String? {
|
open fun findUrlInMessage(): String? {
|
||||||
@@ -176,6 +204,21 @@ open class NewPostViewModel : ViewModel() {
|
|||||||
if (it.selection.collapsed) {
|
if (it.selection.collapsed) {
|
||||||
val lastWord = it.text.substring(0, it.selection.end).substringAfterLast("\n").substringAfterLast(" ")
|
val lastWord = it.text.substring(0, it.selection.end).substringAfterLast("\n").substringAfterLast(" ")
|
||||||
userSuggestionAnchor = it.selection
|
userSuggestionAnchor = it.selection
|
||||||
|
userSuggestionsMainMessage = true
|
||||||
|
if (lastWord.startsWith("@") && lastWord.length > 2) {
|
||||||
|
userSuggestions = LocalCache.findUsersStartingWith(lastWord.removePrefix("@"))
|
||||||
|
} else {
|
||||||
|
userSuggestions = emptyList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun updateZapForwardTo(it: TextFieldValue) {
|
||||||
|
forwardZapToEditting = it
|
||||||
|
if (it.selection.collapsed) {
|
||||||
|
val lastWord = it.text.substring(0, it.selection.end).substringAfterLast("\n").substringAfterLast(" ")
|
||||||
|
userSuggestionAnchor = it.selection
|
||||||
|
userSuggestionsMainMessage = false
|
||||||
if (lastWord.startsWith("@") && lastWord.length > 2) {
|
if (lastWord.startsWith("@") && lastWord.length > 2) {
|
||||||
userSuggestions = LocalCache.findUsersStartingWith(lastWord.removePrefix("@"))
|
userSuggestions = LocalCache.findUsersStartingWith(lastWord.removePrefix("@"))
|
||||||
} else {
|
} else {
|
||||||
@@ -186,15 +229,29 @@ open class NewPostViewModel : ViewModel() {
|
|||||||
|
|
||||||
open fun autocompleteWithUser(item: User) {
|
open fun autocompleteWithUser(item: User) {
|
||||||
userSuggestionAnchor?.let {
|
userSuggestionAnchor?.let {
|
||||||
val lastWord = message.text.substring(0, it.end).substringAfterLast("\n").substringAfterLast(" ")
|
if (userSuggestionsMainMessage == true) {
|
||||||
val lastWordStart = it.end - lastWord.length
|
val lastWord = message.text.substring(0, it.end).substringAfterLast("\n").substringAfterLast(" ")
|
||||||
val wordToInsert = "@${item.pubkeyNpub()}"
|
val lastWordStart = it.end - lastWord.length
|
||||||
|
val wordToInsert = "@${item.pubkeyNpub()}"
|
||||||
|
|
||||||
|
message = TextFieldValue(
|
||||||
|
message.text.replaceRange(lastWordStart, it.end, wordToInsert),
|
||||||
|
TextRange(lastWordStart + wordToInsert.length, lastWordStart + wordToInsert.length)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
val lastWord = forwardZapToEditting.text.substring(0, it.end).substringAfterLast("\n").substringAfterLast(" ")
|
||||||
|
val lastWordStart = it.end - lastWord.length
|
||||||
|
val wordToInsert = "@${item.pubkeyNpub()}"
|
||||||
|
forwardZapTo = item
|
||||||
|
|
||||||
|
forwardZapToEditting = TextFieldValue(
|
||||||
|
forwardZapToEditting.text.replaceRange(lastWordStart, it.end, wordToInsert),
|
||||||
|
TextRange(lastWordStart + wordToInsert.length, lastWordStart + wordToInsert.length)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
message = TextFieldValue(
|
|
||||||
message.text.replaceRange(lastWordStart, it.end, wordToInsert),
|
|
||||||
TextRange(lastWordStart + wordToInsert.length, lastWordStart + wordToInsert.length)
|
|
||||||
)
|
|
||||||
userSuggestionAnchor = null
|
userSuggestionAnchor = null
|
||||||
|
userSuggestionsMainMessage = null
|
||||||
userSuggestions = emptyList()
|
userSuggestions = emptyList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -325,5 +325,5 @@
|
|||||||
|
|
||||||
|
|
||||||
<string name="file_server">File Server</string>
|
<string name="file_server">File Server</string>
|
||||||
|
<string name="zap_forward_lnAddress">LnAddress or @User</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user