From 10b15226a117bf5e6fec34a74ac935b3fde88e28 Mon Sep 17 00:00:00 2001 From: greenart7c3 Date: Wed, 9 Aug 2023 11:45:54 -0300 Subject: [PATCH] added SignerDialog in the like reaction --- .../vitorpamplona/amethyst/model/Account.kt | 16 +++- .../amethyst/service/model/DeletionEvent.kt | 7 ++ .../amethyst/service/model/ReactionEvent.kt | 27 ++++++ .../amethyst/ui/note/ReactionsRow.kt | 96 ++++++++++++++++--- .../ui/screen/loggedIn/AccountViewModel.kt | 16 ++-- 5 files changed, 135 insertions(+), 27 deletions(-) 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 6b225dd2f..6f6723bdb 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt @@ -212,12 +212,12 @@ class Account( return note.hasReacted(userProfile(), reaction) } - fun reactTo(note: Note, reaction: String) { - if (!isWriteable()) return + fun reactTo(note: Note, reaction: String, signEvent: Boolean = true): ReactionEvent? { + if (!isWriteable() && signEvent) return null if (hasReacted(note, reaction)) { // has already liked this note - return + return null } if (note.event is ChatMessageEvent) { @@ -272,6 +272,7 @@ class Account( LocalCache.consume(event) } } + return null } fun createZapRequestFor(note: Note, pollOption: Int?, message: String = "", zapType: LnZapEvent.ZapType): LnZapRequestEvent? { @@ -407,16 +408,21 @@ class Account( delete(listOf(note)) } - fun delete(notes: List) { - if (!isWriteable()) return + fun delete(notes: List, signEvent: Boolean = true): DeletionEvent? { + if (!isWriteable() && signEvent) return null val myNotes = notes.filter { it.author == userProfile() }.map { it.idHex } if (myNotes.isNotEmpty()) { + if (!signEvent) { + return DeletionEvent.create(myNotes, keyPair.pubKey.toHexKey()) + } + val event = DeletionEvent.create(myNotes, keyPair.privKey!!) Client.send(event) LocalCache.consume(event) } + return null } fun createHTTPAuthorization(url: String, method: String, body: String? = null): HTTPAuthorizationEvent? { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/model/DeletionEvent.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/model/DeletionEvent.kt index 2bbf3fce9..a82410481 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/model/DeletionEvent.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/model/DeletionEvent.kt @@ -20,6 +20,13 @@ class DeletionEvent( companion object { const val kind = 5 + fun create(deleteEvents: List, pubKey: HexKey, createdAt: Long = TimeUtils.now()): DeletionEvent { + val content = "" + val tags = deleteEvents.map { listOf("e", it) } + val id = generateId(pubKey, createdAt, kind, tags, content) + return DeletionEvent(id.toHexKey(), pubKey, createdAt, tags, content, "") + } + fun create(deleteEvents: List, privateKey: ByteArray, createdAt: Long = TimeUtils.now()): DeletionEvent { val content = "" val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey() diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/model/ReactionEvent.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/model/ReactionEvent.kt index fc380b2b1..537fafc86 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/model/ReactionEvent.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/model/ReactionEvent.kt @@ -43,6 +43,33 @@ class ReactionEvent( return ReactionEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig.toHexKey()) } + fun create(content: String, originalNote: EventInterface, pubKey: HexKey, createdAt: Long = TimeUtils.now()): ReactionEvent { + var tags = listOf(listOf("e", originalNote.id()), listOf("p", originalNote.pubKey())) + if (originalNote is AddressableEvent) { + tags = tags + listOf(listOf("a", originalNote.address().toTag())) + } + + val id = generateId(pubKey, createdAt, kind, tags, content) + return ReactionEvent(id.toHexKey(), pubKey, createdAt, tags, content, "") + } + + fun create(emojiUrl: EmojiUrl, originalNote: EventInterface, pubKey: HexKey, createdAt: Long = TimeUtils.now()): ReactionEvent { + val content = ":${emojiUrl.code}:" + + var tags = listOf( + listOf("e", originalNote.id()), + listOf("p", originalNote.pubKey()), + listOf("emoji", emojiUrl.code, emojiUrl.url) + ) + + if (originalNote is AddressableEvent) { + tags = tags + listOf(listOf("a", originalNote.address().toTag())) + } + + val id = generateId(pubKey, createdAt, kind, tags, content) + return ReactionEvent(id.toHexKey(), pubKey, createdAt, tags, content, "") + } + fun create(emojiUrl: EmojiUrl, originalNote: EventInterface, privateKey: ByteArray, createdAt: Long = TimeUtils.now()): ReactionEvent { val content = ":${emojiUrl.code}:" val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey() 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 be0802cb5..52cd217ae 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 @@ -75,8 +75,13 @@ import coil.compose.AsyncImage import coil.request.CachePolicy import coil.request.ImageRequest import com.vitorpamplona.amethyst.R +import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.Note +import com.vitorpamplona.amethyst.service.PackageUtils +import com.vitorpamplona.amethyst.service.model.Event +import com.vitorpamplona.amethyst.service.relays.Client import com.vitorpamplona.amethyst.ui.actions.NewPostView +import com.vitorpamplona.amethyst.ui.actions.SignerDialog import com.vitorpamplona.amethyst.ui.components.ImageUrlType import com.vitorpamplona.amethyst.ui.components.InLineIconRenderer import com.vitorpamplona.amethyst.ui.components.TextType @@ -758,6 +763,23 @@ fun LikeReaction( var wantsToChangeReactionSymbol by remember { mutableStateOf(false) } var wantsToReact by remember { mutableStateOf(false) } + var event by remember { mutableStateOf(null) } + + if (event != null) { + SignerDialog( + onClose = { + event = null + }, + onPost = { + scope.launch(Dispatchers.IO) { + Client.send(it) + LocalCache.verifyAndConsume(it, null) + event = null + } + }, + event = event!! + ) + } Box( contentAlignment = Center, @@ -773,6 +795,18 @@ fun LikeReaction( context, onMultipleChoices = { wantsToReact = true + }, + onWantsToSignReaction = { + if (accountViewModel.account.reactionChoices.size == 1) { + val reaction = accountViewModel.account.reactionChoices.first() + if (accountViewModel.hasReactedTo(baseNote, reaction)) { + event = accountViewModel.deleteReactionTo(baseNote, reaction, false) + } else { + event = accountViewModel.reactTo(baseNote, reaction, false) + } + } else if (accountViewModel.account.reactionChoices.size > 1) { + wantsToReact = true + } } ) }, @@ -917,7 +951,8 @@ private fun likeClick( accountViewModel: AccountViewModel, scope: CoroutineScope, context: Context, - onMultipleChoices: () -> Unit + onMultipleChoices: () -> Unit, + onWantsToSignReaction: () -> Unit ) { if (accountViewModel.account.reactionChoices.isEmpty()) { scope.launch { @@ -930,14 +965,18 @@ private fun likeClick( .show() } } else if (!accountViewModel.isWriteable()) { - scope.launch { - Toast - .makeText( - context, - context.getString(R.string.login_with_a_private_key_to_like_posts), - Toast.LENGTH_SHORT - ) - .show() + if (PackageUtils.isAmberInstalled(context)) { + onWantsToSignReaction() + } else { + scope.launch { + Toast + .makeText( + context, + context.getString(R.string.login_with_a_private_key_to_like_posts), + Toast.LENGTH_SHORT + ) + .show() + } } } else if (accountViewModel.account.reactionChoices.size == 1) { scope.launch(Dispatchers.IO) { @@ -1313,16 +1352,39 @@ private fun ActionableReactionButton( toRemove: Set ) { val scope = rememberCoroutineScope() + val context = LocalContext.current + var event by remember { mutableStateOf(null) } + if (event != null) { + SignerDialog( + onClose = { + event = null + onDismiss() + }, + onPost = { + scope.launch(Dispatchers.IO) { + Client.send(it) + LocalCache.verifyAndConsume(it, null) + event = null + onDismiss() + } + }, + event = event!! + ) + } Button( modifier = Modifier.padding(horizontal = 3.dp), onClick = { + val isAmberInstalled = PackageUtils.isAmberInstalled(context) scope.launch(Dispatchers.IO) { - accountViewModel.reactToOrDelete( + event = accountViewModel.reactToOrDelete( baseNote, - reactionType + reactionType, + !isAmberInstalled ) - onDismiss() + if (!isAmberInstalled) { + onDismiss() + } } }, shape = ButtonBorder, @@ -1334,12 +1396,16 @@ private fun ActionableReactionButton( val thisModifier = remember(reactionType) { Modifier.combinedClickable( onClick = { + val isAmberInstalled = PackageUtils.isAmberInstalled(context) scope.launch(Dispatchers.IO) { - accountViewModel.reactToOrDelete( + event = accountViewModel.reactToOrDelete( baseNote, - reactionType + reactionType, + !isAmberInstalled ) - onDismiss() + if (!isAmberInstalled) { + onDismiss() + } } }, onLongClick = { 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 480e63e23..a40cce74e 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 @@ -21,10 +21,12 @@ import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.model.UserState import com.vitorpamplona.amethyst.service.lnurl.LightningAddressResolver +import com.vitorpamplona.amethyst.service.model.DeletionEvent import com.vitorpamplona.amethyst.service.model.Event import com.vitorpamplona.amethyst.service.model.GiftWrapEvent import com.vitorpamplona.amethyst.service.model.LnZapEvent import com.vitorpamplona.amethyst.service.model.PayInvoiceErrorResponse +import com.vitorpamplona.amethyst.service.model.ReactionEvent import com.vitorpamplona.amethyst.service.model.ReportEvent import com.vitorpamplona.amethyst.service.model.SealedGossipEvent import kotlinx.collections.immutable.ImmutableSet @@ -87,16 +89,16 @@ class AccountViewModel(val account: Account) : ViewModel() { return account.userProfile() } - fun reactTo(note: Note, reaction: String) { - account.reactTo(note, reaction) + fun reactTo(note: Note, reaction: String, signEvent: Boolean = true): ReactionEvent? { + return account.reactTo(note, reaction, signEvent) } - fun reactToOrDelete(note: Note, reaction: String) { + fun reactToOrDelete(note: Note, reaction: String, signEvent: Boolean = true): Event? { val currentReactions = account.reactionTo(note, reaction) if (currentReactions.isNotEmpty()) { - account.delete(currentReactions) + return account.delete(currentReactions, signEvent) } else { - account.reactTo(note, reaction) + return account.reactTo(note, reaction, signEvent) } } @@ -109,8 +111,8 @@ class AccountViewModel(val account: Account) : ViewModel() { return account.hasReacted(baseNote, reaction) } - fun deleteReactionTo(note: Note, reaction: String) { - account.delete(account.reactionTo(note, reaction)) + fun deleteReactionTo(note: Note, reaction: String, signEvent: Boolean = true): DeletionEvent? { + return account.delete(account.reactionTo(note, reaction), signEvent) } fun hasBoosted(baseNote: Note): Boolean {