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 f287b3011..178e5f264 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt @@ -137,7 +137,7 @@ class Account( } } - fun createZapRequestFor(note: Note, pollOption: Int?, message: String = ""): LnZapRequestEvent? { + fun createZapRequestFor(note: Note, pollOption: Int?, message: String = "", zapType: LnZapEvent.ZapType): LnZapRequestEvent? { if (!isWriteable()) return null note.event?.let { event -> @@ -147,7 +147,8 @@ class Account( ?: localRelays.map { it.url }.toSet(), loggedIn.privKey!!, pollOption, - message + message, + zapType ) } return null @@ -171,14 +172,15 @@ class Account( return createZapRequestFor(user) } - fun createZapRequestFor(userPubKeyHex: String, message: String = ""): LnZapRequestEvent? { + fun createZapRequestFor(userPubKeyHex: String, message: String = "", zapType: LnZapEvent.ZapType): LnZapRequestEvent? { if (!isWriteable()) return null return LnZapRequestEvent.create( userPubKeyHex, userProfile().latestContactList?.relays()?.keys?.ifEmpty { null } ?: localRelays.map { it.url }.toSet(), loggedIn.privKey!!, - message + message, + zapType ) } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapEvent.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapEvent.kt index ad83690e9..1812fb803 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapEvent.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/model/LnZapEvent.kt @@ -69,4 +69,11 @@ class LnZapEvent( companion object { const val kind = 9735 } + + enum class ZapType() { + PUBLIC, + PRIVATE, // not yet implemented + ANONYMOUS, + NONZAP + } } 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 5452aa36a..34fddc02a 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 @@ -25,10 +25,12 @@ class LnZapRequestEvent( privateKey: ByteArray, pollOption: Int?, message: String, + zapType: LnZapEvent.ZapType, createdAt: Long = Date().time / 1000 ): LnZapRequestEvent { val content = message - val pubKey = Utils.pubkeyCreate(privateKey).toHexKey() + var privkey = privateKey + var pubKey = Utils.pubkeyCreate(privateKey).toHexKey() var tags = listOf( listOf("e", originalNote.id()), listOf("p", originalNote.pubKey()), @@ -40,9 +42,13 @@ class LnZapRequestEvent( if (pollOption != null && pollOption >= 0) { tags = tags + listOf(listOf(POLL_OPTION, pollOption.toString())) } - + if (zapType == LnZapEvent.ZapType.ANONYMOUS) { + tags = tags + listOf(listOf("anon", "")) + privkey = Utils.privkeyCreate() + pubKey = Utils.pubkeyCreate(privkey).toHexKey() + } val id = generateId(pubKey, createdAt, kind, tags, content) - val sig = Utils.sign(id, privateKey) + val sig = Utils.sign(id, privkey) return LnZapRequestEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig.toHexKey()) } @@ -51,17 +57,24 @@ class LnZapRequestEvent( relays: Set, privateKey: ByteArray, message: String, + zapType: LnZapEvent.ZapType, createdAt: Long = Date().time / 1000 ): LnZapRequestEvent { val content = message - val pubKey = Utils.pubkeyCreate(privateKey).toHexKey() - val tags = listOf( + var privkey = privateKey + var pubKey = Utils.pubkeyCreate(privateKey).toHexKey() + var tags = listOf( listOf("p", userHex), listOf("relays") + relays ) + if (zapType == LnZapEvent.ZapType.ANONYMOUS) { + tags = tags + listOf(listOf("anon", "")) + privkey = Utils.privkeyCreate() + pubKey = Utils.pubkeyCreate(privkey).toHexKey() + } val id = generateId(pubKey, createdAt, kind, tags, content) - val sig = Utils.sign(id, privateKey) + val sig = Utils.sign(id, privkey) return LnZapRequestEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig.toHexKey()) } } 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 bca274b3c..06e38c773 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 @@ -37,6 +37,7 @@ import androidx.compose.ui.unit.sp import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.service.lnurl.LightningAddressResolver +import com.vitorpamplona.amethyst.service.model.LnZapEvent import kotlinx.coroutines.launch @Composable @@ -135,7 +136,7 @@ fun InvoiceRequest( Button( modifier = Modifier.fillMaxWidth().padding(vertical = 10.dp), onClick = { - val zapRequest = account.createZapRequestFor(toUserPubKeyHex, message) + val zapRequest = account.createZapRequestFor(toUserPubKeyHex, message, LnZapEvent.ZapType.PUBLIC) LightningAddressResolver().lnAddressInvoice( lud16, diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/PollNote.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/PollNote.kt index e46645a34..da0f35936 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/PollNote.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/PollNote.kt @@ -31,6 +31,7 @@ import androidx.compose.ui.window.Popup import androidx.navigation.NavController import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.model.Note +import com.vitorpamplona.amethyst.service.model.LnZapEvent import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange @@ -251,7 +252,8 @@ fun ZapVote( scope.launch(Dispatchers.Main) { zappingProgress = it } - } + }, + zapType = LnZapEvent.ZapType.PUBLIC ) } } else { @@ -378,7 +380,8 @@ fun FilteredZapAmountChoicePopup( zapMessage, context, onError, - onProgress + onProgress, + LnZapEvent.ZapType.PUBLIC ) onDismiss() } @@ -403,7 +406,8 @@ fun FilteredZapAmountChoicePopup( zapMessage, context, onError, - onProgress + onProgress, + LnZapEvent.ZapType.PUBLIC ) onDismiss() } @@ -495,7 +499,8 @@ fun ZapVoteAmountChoicePopup( "", context, onError, - onProgress + onProgress, + LnZapEvent.ZapType.PUBLIC ) onDismiss() } @@ -520,7 +525,8 @@ fun ZapVoteAmountChoicePopup( "", context, onError, - onProgress + onProgress, + LnZapEvent.ZapType.PUBLIC ) onDismiss() } 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 e90641a38..5a0d7a3b4 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 @@ -55,6 +55,7 @@ import coil.request.CachePolicy import coil.request.ImageRequest import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.model.Note +import com.vitorpamplona.amethyst.service.model.LnZapEvent import com.vitorpamplona.amethyst.ui.actions.NewPostView import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange @@ -363,7 +364,8 @@ fun ZapReaction( scope.launch(Dispatchers.Main) { zappingProgress = it } - } + }, + zapType = LnZapEvent.ZapType.PUBLIC ) } } else if (account.zapAmountChoices.size > 1) { @@ -559,7 +561,8 @@ fun ZapAmountChoicePopup( zapMessage, context, onError, - onProgress + onProgress, + LnZapEvent.ZapType.PUBLIC ) onDismiss() } @@ -584,7 +587,8 @@ fun ZapAmountChoicePopup( zapMessage, context, onError, - onProgress + onProgress, + LnZapEvent.ZapType.PUBLIC ) onDismiss() } 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 index b0ea32a75..5fe601a6a 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapCustomDialog.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapCustomDialog.kt @@ -5,12 +5,7 @@ import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape 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.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -27,15 +22,16 @@ import androidx.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.Note +import com.vitorpamplona.amethyst.service.model.LnZapEvent import com.vitorpamplona.amethyst.ui.actions.CloseButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel +import com.vitorpamplona.amethyst.ui.screen.loggedIn.TextSpinner import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch class ZapOptionstViewModel : ViewModel() { private var account: Account? = null - - var customAmount by mutableStateOf(TextFieldValue("1000")) + var customAmount by mutableStateOf(TextFieldValue("21")) var customMessage by mutableStateOf(TextFieldValue("")) fun load(account: Account) { @@ -63,11 +59,21 @@ fun ZapCustomDialog(onClose: () -> Unit, account: Account, accountViewModel: Acc val context = LocalContext.current val scope = rememberCoroutineScope() val postViewModel: ZapOptionstViewModel = viewModel() - LaunchedEffect(account) { postViewModel.load(account) } + var zappingProgress by remember { mutableStateOf(0f) } + + val zapTypes = listOf( + Pair(LnZapEvent.ZapType.PUBLIC, "Public"), + Pair(LnZapEvent.ZapType.ANONYMOUS, "Anonymous"), + Pair(LnZapEvent.ZapType.NONZAP, "Non-Zap") + ) + + val zapOptions = zapTypes.map { it.second } + var selectedZapType by remember { mutableStateOf(zapTypes[0]) } + Dialog( onDismissRequest = { onClose() }, properties = DialogProperties( @@ -98,6 +104,7 @@ fun ZapCustomDialog(onClose: () -> Unit, account: Account, accountViewModel: Acc postViewModel.customMessage.text, context, onError = { + zappingProgress = 0f scope.launch { Toast .makeText(context, it, Toast.LENGTH_SHORT).show() @@ -105,8 +112,10 @@ fun ZapCustomDialog(onClose: () -> Unit, account: Account, accountViewModel: Acc }, onProgress = { scope.launch(Dispatchers.Main) { + zappingProgress = it } - } + }, + zapType = selectedZapType.first ) } onClose() @@ -174,6 +183,15 @@ fun ZapCustomDialog(onClose: () -> Unit, account: Account, accountViewModel: Acc .weight(1f) ) } + TextSpinner( + label = "Zap Type", + placeholder = "Public", + options = zapOptions, + onSelect = { + selectedZapType = zapTypes[it] + }, + modifier = Modifier.fillMaxWidth() + ) } } } 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 e9f777be1..c88352b6c 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 @@ -14,6 +14,7 @@ import com.vitorpamplona.amethyst.model.AccountState import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.service.lnurl.LightningAddressResolver +import com.vitorpamplona.amethyst.service.model.LnZapEvent import com.vitorpamplona.amethyst.service.model.ReportEvent import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay @@ -52,7 +53,7 @@ class AccountViewModel(private val account: Account) : ViewModel() { account.delete(account.boostsTo(note)) } - fun zap(note: Note, amount: Long, pollOption: Int?, message: String, context: Context, onError: (String) -> Unit, onProgress: (percent: Float) -> Unit) { + fun zap(note: Note, amount: Long, pollOption: Int?, message: String, context: Context, onError: (String) -> Unit, onProgress: (percent: Float) -> Unit, zapType: LnZapEvent.ZapType) { val lud16 = note.author?.info?.lud16?.trim() ?: note.author?.info?.lud06?.trim() if (lud16.isNullOrBlank()) { @@ -60,7 +61,14 @@ class AccountViewModel(private val account: Account) : ViewModel() { return } - val zapRequest = account.createZapRequestFor(note, pollOption, message) + var zapRequestJson = "" + + if (zapType != LnZapEvent.ZapType.NONZAP) { + val zapRequest = account.createZapRequestFor(note, pollOption, message, zapType) + if (zapRequest != null) { + zapRequestJson = zapRequest.toJson() + } + } onProgress(0.10f) @@ -68,7 +76,7 @@ class AccountViewModel(private val account: Account) : ViewModel() { lud16, amount, message, - zapRequest?.toJson(), + zapRequestJson, onSuccess = { onProgress(0.7f) if (account.hasWalletConnectSetup()) {