added SignerDialog in the like reaction
This commit is contained in:
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user