diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt index 2fa0088c7..7c121402c 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt @@ -152,11 +152,11 @@ class Account( } } - fun createZapRequestFor(note: Note): LnZapRequestEvent? { + fun createZapRequestFor(note: Note, message: String = ""): LnZapRequestEvent? { if (!isWriteable()) return null note.event?.let { - return LnZapRequestEvent.create(it, userProfile().latestContactList?.relays()?.keys?.ifEmpty { null } ?: localRelays.map { it.url }.toSet(), loggedIn.privKey!!) + return LnZapRequestEvent.create(it, userProfile().latestContactList?.relays()?.keys?.ifEmpty { null } ?: localRelays.map { it.url }.toSet(), loggedIn.privKey!!, message) } return null @@ -180,10 +180,10 @@ class Account( return createZapRequestFor(user.pubkeyHex) } - fun createZapRequestFor(userPubKeyHex: String): LnZapRequestEvent? { + fun createZapRequestFor(userPubKeyHex: String, message: String = ""): LnZapRequestEvent? { if (!isWriteable()) return null - return LnZapRequestEvent.create(userPubKeyHex, userProfile().latestContactList?.relays()?.keys?.ifEmpty { null } ?: localRelays.map { it.url }.toSet(), loggedIn.privKey!!) + return LnZapRequestEvent.create(userPubKeyHex, userProfile().latestContactList?.relays()?.keys?.ifEmpty { null } ?: localRelays.map { it.url }.toSet(), loggedIn.privKey!!, message) } fun report(note: Note, type: ReportEvent.ReportType, content: String = "") { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapRequestEvent.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapRequestEvent.kt index 8863b7af4..23dc9d6b6 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapRequestEvent.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapRequestEvent.kt @@ -23,9 +23,10 @@ class LnZapRequestEvent( originalNote: EventInterface, relays: Set, privateKey: ByteArray, + message: String, createdAt: Long = Date().time / 1000 ): LnZapRequestEvent { - val content = "" + val content = message val pubKey = Utils.pubkeyCreate(privateKey).toHexKey() var tags = listOf( listOf("e", originalNote.id()), @@ -45,9 +46,10 @@ class LnZapRequestEvent( userHex: String, relays: Set, privateKey: ByteArray, + message: String, createdAt: Long = Date().time / 1000 ): LnZapRequestEvent { - val content = "" + val content = message val pubKey = Utils.pubkeyCreate(privateKey).toHexKey() val tags = listOf( listOf("p", userHex), diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/InvoiceRequest.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/InvoiceRequest.kt index 8cf0b80c4..2fff33f83 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/InvoiceRequest.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/InvoiceRequest.kt @@ -130,7 +130,7 @@ fun InvoiceRequest(lud16: String, toUserPubKeyHex: String, account: Account, onC Button( modifier = Modifier.fillMaxWidth().padding(vertical = 10.dp), onClick = { - val zapRequest = account.createZapRequestFor(toUserPubKeyHex) + val zapRequest = account.createZapRequestFor(toUserPubKeyHex, message) LightningAddressResolver().lnAddressInvoice( lud16, diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt index b9b6be39b..991bdc668 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt @@ -303,10 +303,11 @@ fun ZapReaction( val zapsState by baseNote.live().zaps.observeAsState() val zappedNote = zapsState?.note + val zapMessage = "" var wantsToZap by remember { mutableStateOf(false) } var wantsToChangeZapAmount by remember { mutableStateOf(false) } - + var wantsToSetCustomZap by remember { mutableStateOf(false) } val grayTint = MaterialTheme.colors.onSurface.copy(alpha = 0.32f) val context = LocalContext.current val scope = rememberCoroutineScope() @@ -347,7 +348,7 @@ fun ZapReaction( accountViewModel.zap( baseNote, account.zapAmountChoices.first() * 1000, - "", + zapMessage, context, onError = { scope.launch { @@ -370,6 +371,9 @@ fun ZapReaction( }, onLongClick = { wantsToChangeZapAmount = true + }, + onDoubleClick = { + wantsToSetCustomZap = true } ) ) { @@ -402,6 +406,10 @@ fun ZapReaction( UpdateZapAmountDialog({ wantsToChangeZapAmount = false }, account = account) } + if (wantsToSetCustomZap) { + ZapCustomDialog({ wantsToSetCustomZap = false }, account = account, accountViewModel, baseNote) + } + if (zappedNote?.isZappedBy(account.userProfile()) == true) { zappingProgress = 1f Icon( @@ -530,7 +538,7 @@ fun ZapAmountChoicePopup( val accountState by accountViewModel.accountLiveData.observeAsState() val account = accountState?.account ?: return - + val zapMessage = "" val scope = rememberCoroutineScope() Popup( @@ -547,7 +555,7 @@ fun ZapAmountChoicePopup( accountViewModel.zap( baseNote, amountInSats * 1000, - "", + zapMessage, context, onError, onProgress @@ -571,7 +579,7 @@ fun ZapAmountChoicePopup( accountViewModel.zap( baseNote, amountInSats * 1000, - "", + zapMessage, context, onError, onProgress diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapCustomDialog.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapCustomDialog.kt new file mode 100644 index 000000000..09c9e8e63 --- /dev/null +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapCustomDialog.kt @@ -0,0 +1,193 @@ +package com.vitorpamplona.amethyst.ui.note + +import android.widget.Toast +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +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.text.font.FontWeight +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.unit.dp +import androidx.compose.ui.unit.sp +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.model.Account +import com.vitorpamplona.amethyst.model.Note +import com.vitorpamplona.amethyst.ui.actions.CloseButton +import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +class ZapOptionstViewModel : ViewModel() { + private var account: Account? = null + + var customAmount by mutableStateOf(TextFieldValue("1000")) + var customMessage by mutableStateOf(TextFieldValue("")) + + fun load(account: Account) { + this.account = account + } + + fun cancel() { + } +} + +fun isNumeric(toCheck: String): Boolean { + return toCheck.toLongOrNull() != null +} + +@OptIn(ExperimentalLayoutApi::class) +@Composable +fun ZapCustomDialog(onClose: () -> Unit, account: Account, accountViewModel: AccountViewModel, baseNote: Note) { + val context = LocalContext.current + val scope = rememberCoroutineScope() + val postViewModel: ZapOptionstViewModel = 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() + }) + } + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 5.dp), + horizontalArrangement = Arrangement.Start, + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + painter = painterResource(com.vitorpamplona.amethyst.R.drawable.zap), + null, + modifier = Modifier.size(20.dp), + tint = Color.Unspecified + ) + Text( + text = "Custom Zap", + fontSize = 20.sp, + fontWeight = FontWeight.W500, + modifier = Modifier.padding(start = 10.dp) + ) + } + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 5.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + OutlinedTextField( + // stringResource(R.string.new_amount_in_sats + label = { Text(text = "Custom Amount") }, + value = postViewModel.customAmount, + onValueChange = { + if (isNumeric(it.text)) { + postViewModel.customAmount = it + } + }, + keyboardOptions = KeyboardOptions.Default.copy( + capitalization = KeyboardCapitalization.None, + keyboardType = KeyboardType.Number + ), + placeholder = { + Text( + text = postViewModel.customAmount.text, + color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f) + ) + }, + singleLine = true, + modifier = Modifier + .padding(end = 10.dp) + .weight(1f) + ) + } + Spacer(modifier = Modifier.height(10.dp)) + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 5.dp), + verticalAlignment = Alignment.CenterVertically + ) { + OutlinedTextField( + // stringResource(R.string.new_amount_in_sats + label = { Text(text = "Message") }, + value = postViewModel.customMessage, + onValueChange = { + postViewModel.customMessage = it + }, + keyboardOptions = KeyboardOptions.Default.copy( + capitalization = KeyboardCapitalization.None, + keyboardType = KeyboardType.Text + ), + placeholder = { + Text( + text = postViewModel.customMessage.text, + color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f) + ) + }, + singleLine = true, + modifier = Modifier + .padding(end = 10.dp) + .weight(1f) + ) + + Button( + onClick = { + scope.launch(Dispatchers.IO) { + accountViewModel.zap( + baseNote, + postViewModel.customAmount.text.toLong() * 1000, + postViewModel.customMessage.text, + context, + onError = { + Toast + .makeText(context, it, Toast.LENGTH_SHORT).show() + }, + onProgress = { + scope.launch(Dispatchers.Main) { + } + } + ) + } + onClose() + } + ) { + Text(text = "⚡Zap ", color = Color.White) + } + } + } + } + } +} diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt index 30ebf2b30..ce1372dfe 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt @@ -60,7 +60,7 @@ class AccountViewModel(private val account: Account) : ViewModel() { return } - val zapRequest = account.createZapRequestFor(note) + val zapRequest = account.createZapRequestFor(note, message) onProgress(0.10f)