added SignerDialog in the like reaction

This commit is contained in:
greenart7c3
2023-08-09 11:45:54 -03:00
parent 7a46376bc1
commit 10b15226a1
5 changed files with 135 additions and 27 deletions
@@ -212,12 +212,12 @@ class Account(
return note.hasReacted(userProfile(), reaction) return note.hasReacted(userProfile(), reaction)
} }
fun reactTo(note: Note, reaction: String) { fun reactTo(note: Note, reaction: String, signEvent: Boolean = true): ReactionEvent? {
if (!isWriteable()) return if (!isWriteable() && signEvent) return null
if (hasReacted(note, reaction)) { if (hasReacted(note, reaction)) {
// has already liked this note // has already liked this note
return return null
} }
if (note.event is ChatMessageEvent) { if (note.event is ChatMessageEvent) {
@@ -272,6 +272,7 @@ class Account(
LocalCache.consume(event) LocalCache.consume(event)
} }
} }
return null
} }
fun createZapRequestFor(note: Note, pollOption: Int?, message: String = "", zapType: LnZapEvent.ZapType): LnZapRequestEvent? { fun createZapRequestFor(note: Note, pollOption: Int?, message: String = "", zapType: LnZapEvent.ZapType): LnZapRequestEvent? {
@@ -407,16 +408,21 @@ class Account(
delete(listOf(note)) delete(listOf(note))
} }
fun delete(notes: List<Note>) { fun delete(notes: List<Note>, signEvent: Boolean = true): DeletionEvent? {
if (!isWriteable()) return if (!isWriteable() && signEvent) return null
val myNotes = notes.filter { it.author == userProfile() }.map { it.idHex } val myNotes = notes.filter { it.author == userProfile() }.map { it.idHex }
if (myNotes.isNotEmpty()) { if (myNotes.isNotEmpty()) {
if (!signEvent) {
return DeletionEvent.create(myNotes, keyPair.pubKey.toHexKey())
}
val event = DeletionEvent.create(myNotes, keyPair.privKey!!) val event = DeletionEvent.create(myNotes, keyPair.privKey!!)
Client.send(event) Client.send(event)
LocalCache.consume(event) LocalCache.consume(event)
} }
return null
} }
fun createHTTPAuthorization(url: String, method: String, body: String? = null): HTTPAuthorizationEvent? { fun createHTTPAuthorization(url: String, method: String, body: String? = null): HTTPAuthorizationEvent? {
@@ -20,6 +20,13 @@ class DeletionEvent(
companion object { companion object {
const val kind = 5 const val kind = 5
fun create(deleteEvents: List<String>, 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<String>, privateKey: ByteArray, createdAt: Long = TimeUtils.now()): DeletionEvent { fun create(deleteEvents: List<String>, privateKey: ByteArray, createdAt: Long = TimeUtils.now()): DeletionEvent {
val content = "" val content = ""
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey() val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
@@ -43,6 +43,33 @@ class ReactionEvent(
return ReactionEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig.toHexKey()) 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 { fun create(emojiUrl: EmojiUrl, originalNote: EventInterface, privateKey: ByteArray, createdAt: Long = TimeUtils.now()): ReactionEvent {
val content = ":${emojiUrl.code}:" val content = ":${emojiUrl.code}:"
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey() val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
@@ -75,8 +75,13 @@ import coil.compose.AsyncImage
import coil.request.CachePolicy import coil.request.CachePolicy
import coil.request.ImageRequest import coil.request.ImageRequest
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note 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.NewPostView
import com.vitorpamplona.amethyst.ui.actions.SignerDialog
import com.vitorpamplona.amethyst.ui.components.ImageUrlType import com.vitorpamplona.amethyst.ui.components.ImageUrlType
import com.vitorpamplona.amethyst.ui.components.InLineIconRenderer import com.vitorpamplona.amethyst.ui.components.InLineIconRenderer
import com.vitorpamplona.amethyst.ui.components.TextType import com.vitorpamplona.amethyst.ui.components.TextType
@@ -758,6 +763,23 @@ fun LikeReaction(
var wantsToChangeReactionSymbol by remember { mutableStateOf(false) } var wantsToChangeReactionSymbol by remember { mutableStateOf(false) }
var wantsToReact by remember { mutableStateOf(false) } var wantsToReact by remember { mutableStateOf(false) }
var event by remember { mutableStateOf<Event?>(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( Box(
contentAlignment = Center, contentAlignment = Center,
@@ -773,6 +795,18 @@ fun LikeReaction(
context, context,
onMultipleChoices = { onMultipleChoices = {
wantsToReact = true 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, accountViewModel: AccountViewModel,
scope: CoroutineScope, scope: CoroutineScope,
context: Context, context: Context,
onMultipleChoices: () -> Unit onMultipleChoices: () -> Unit,
onWantsToSignReaction: () -> Unit
) { ) {
if (accountViewModel.account.reactionChoices.isEmpty()) { if (accountViewModel.account.reactionChoices.isEmpty()) {
scope.launch { scope.launch {
@@ -930,14 +965,18 @@ private fun likeClick(
.show() .show()
} }
} else if (!accountViewModel.isWriteable()) { } else if (!accountViewModel.isWriteable()) {
scope.launch { if (PackageUtils.isAmberInstalled(context)) {
Toast onWantsToSignReaction()
.makeText( } else {
context, scope.launch {
context.getString(R.string.login_with_a_private_key_to_like_posts), Toast
Toast.LENGTH_SHORT .makeText(
) context,
.show() context.getString(R.string.login_with_a_private_key_to_like_posts),
Toast.LENGTH_SHORT
)
.show()
}
} }
} else if (accountViewModel.account.reactionChoices.size == 1) { } else if (accountViewModel.account.reactionChoices.size == 1) {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
@@ -1313,16 +1352,39 @@ private fun ActionableReactionButton(
toRemove: Set<String> toRemove: Set<String>
) { ) {
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val context = LocalContext.current
var event by remember { mutableStateOf<Event?>(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( Button(
modifier = Modifier.padding(horizontal = 3.dp), modifier = Modifier.padding(horizontal = 3.dp),
onClick = { onClick = {
val isAmberInstalled = PackageUtils.isAmberInstalled(context)
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
accountViewModel.reactToOrDelete( event = accountViewModel.reactToOrDelete(
baseNote, baseNote,
reactionType reactionType,
!isAmberInstalled
) )
onDismiss() if (!isAmberInstalled) {
onDismiss()
}
} }
}, },
shape = ButtonBorder, shape = ButtonBorder,
@@ -1334,12 +1396,16 @@ private fun ActionableReactionButton(
val thisModifier = remember(reactionType) { val thisModifier = remember(reactionType) {
Modifier.combinedClickable( Modifier.combinedClickable(
onClick = { onClick = {
val isAmberInstalled = PackageUtils.isAmberInstalled(context)
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
accountViewModel.reactToOrDelete( event = accountViewModel.reactToOrDelete(
baseNote, baseNote,
reactionType reactionType,
!isAmberInstalled
) )
onDismiss() if (!isAmberInstalled) {
onDismiss()
}
} }
}, },
onLongClick = { onLongClick = {
@@ -21,10 +21,12 @@ import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.model.UserState import com.vitorpamplona.amethyst.model.UserState
import com.vitorpamplona.amethyst.service.lnurl.LightningAddressResolver 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.Event
import com.vitorpamplona.amethyst.service.model.GiftWrapEvent import com.vitorpamplona.amethyst.service.model.GiftWrapEvent
import com.vitorpamplona.amethyst.service.model.LnZapEvent import com.vitorpamplona.amethyst.service.model.LnZapEvent
import com.vitorpamplona.amethyst.service.model.PayInvoiceErrorResponse 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.ReportEvent
import com.vitorpamplona.amethyst.service.model.SealedGossipEvent import com.vitorpamplona.amethyst.service.model.SealedGossipEvent
import kotlinx.collections.immutable.ImmutableSet import kotlinx.collections.immutable.ImmutableSet
@@ -87,16 +89,16 @@ class AccountViewModel(val account: Account) : ViewModel() {
return account.userProfile() return account.userProfile()
} }
fun reactTo(note: Note, reaction: String) { fun reactTo(note: Note, reaction: String, signEvent: Boolean = true): ReactionEvent? {
account.reactTo(note, reaction) 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) val currentReactions = account.reactionTo(note, reaction)
if (currentReactions.isNotEmpty()) { if (currentReactions.isNotEmpty()) {
account.delete(currentReactions) return account.delete(currentReactions, signEvent)
} else { } 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) return account.hasReacted(baseNote, reaction)
} }
fun deleteReactionTo(note: Note, reaction: String) { fun deleteReactionTo(note: Note, reaction: String, signEvent: Boolean = true): DeletionEvent? {
account.delete(account.reactionTo(note, reaction)) return account.delete(account.reactionTo(note, reaction), signEvent)
} }
fun hasBoosted(baseNote: Note): Boolean { fun hasBoosted(baseNote: Note): Boolean {