wip: reaction
This commit is contained in:
@@ -144,12 +144,15 @@ class MessageManager(private val nostr: Nostr) {
|
||||
|
||||
private suspend fun setCachedRumor(giftId: EventId, rumor: UnsignedEvent) {
|
||||
try {
|
||||
val isReaction = rumor.kind().asStd() == KindStandard.REACTION
|
||||
val kValue = if (isReaction) "reaction" else "dm"
|
||||
|
||||
// Construct reference tags
|
||||
val tags = listOf(
|
||||
Tag.identifier(giftId.toHex()),
|
||||
Tag.publicKey(rumor.author()),
|
||||
Tag.custom("r", listOf(rumor.roomId().toString())),
|
||||
Tag.custom("k", listOf("14"))
|
||||
Tag.custom("k", listOf("14", kValue))
|
||||
)
|
||||
|
||||
// Set event kind
|
||||
@@ -348,4 +351,64 @@ class MessageManager(private val nostr: Nostr) {
|
||||
throw IllegalStateException("Failed to send message: ${e.message}", e)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun sendReaction(
|
||||
to: Set<PublicKey>,
|
||||
targetEventId: EventId,
|
||||
reaction: String,
|
||||
onRumorCreated: ((UnsignedEvent) -> Unit)? = null,
|
||||
) {
|
||||
try {
|
||||
val currentUser =
|
||||
signer.getPublicKeyAsync() ?: throw IllegalStateException("User not signed in")
|
||||
|
||||
val tags = mutableListOf<Tag>()
|
||||
tags.add(Tag.event(targetEventId))
|
||||
// Add public key tags for each recipient (including me) to ensure roomId consistency
|
||||
to.forEach { pubkey ->
|
||||
tags.add(Tag.publicKey(pubkey))
|
||||
}
|
||||
|
||||
for (receiver in setOf(currentUser) + to) {
|
||||
// Construct the rumor event
|
||||
val rumor = EventBuilder(Kind.fromStd(KindStandard.REACTION), reaction)
|
||||
.tags(tags)
|
||||
.finalizeUnsigned(currentUser)
|
||||
.ensureId()
|
||||
|
||||
// Emit the rumor to the chat screen
|
||||
if (receiver == currentUser) {
|
||||
onRumorCreated?.invoke(rumor)
|
||||
}
|
||||
|
||||
// Construct the gift wrap event
|
||||
val gift = nip59MakeGiftWrapAsync(
|
||||
signer = signer,
|
||||
receiverPubkey = receiver,
|
||||
rumor = rumor,
|
||||
extraTags = listOf(
|
||||
Tag.custom("k", listOf("14"))
|
||||
)
|
||||
)
|
||||
|
||||
// Send the event to receiver's NIP-17 relays
|
||||
val output = client?.sendEvent(
|
||||
event = gift,
|
||||
target = SendEventTarget.toNip17(),
|
||||
ackPolicy = AckPolicy.none(),
|
||||
authenticationTimeout = Duration.parse("2s")
|
||||
)
|
||||
|
||||
if (output != null) {
|
||||
// Keep track of rumor IDs
|
||||
val id = rumor.id() ?: throw IllegalStateException("Rumor ID is null")
|
||||
rumorMap[id] = output.id
|
||||
}
|
||||
}
|
||||
} catch (e: CancellationException) {
|
||||
throw e
|
||||
} catch (e: Exception) {
|
||||
throw IllegalStateException("Failed to send reaction: ${e.message}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,8 +241,29 @@ class ChatRepository(
|
||||
}
|
||||
}
|
||||
|
||||
fun sendReaction(roomId: Long, targetEventId: EventId, reaction: String) {
|
||||
scope.launch(defaultDispatcher) {
|
||||
try {
|
||||
val room = getChatRoom(roomId) ?: throw IllegalArgumentException("Room not found")
|
||||
nostr.messages.sendReaction(
|
||||
to = room.members,
|
||||
targetEventId = targetEventId,
|
||||
reaction = reaction,
|
||||
onRumorCreated = {
|
||||
scope.launch(defaultDispatcher) {
|
||||
updateRoomState(it, roomId)
|
||||
}
|
||||
},
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
showError("Error: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun updateRoomState(event: UnsignedEvent, roomId: Long = event.roomId()) {
|
||||
val currentUser = nostr.signer.getPublicKeyAsync() ?: return
|
||||
val isReaction = event.kind().asStd() == KindStandard.REACTION
|
||||
|
||||
_state.update { currentState ->
|
||||
val rooms = currentState.rooms.toMutableMap()
|
||||
@@ -256,10 +277,11 @@ class ChatRepository(
|
||||
// New room discovery
|
||||
val newRoom = Room.new(event, currentUser, roomId).copy(
|
||||
kind = newKind,
|
||||
unreadCount = if (isFromMe) 0 else 1
|
||||
unreadCount = if (isFromMe || isReaction) 0 else 1,
|
||||
lastMessage = if (isReaction) null else event.content()
|
||||
)
|
||||
rooms[newRoom.id] = newRoom
|
||||
} else if (event.createdAt().asSecs() >= existingRoom.createdAt.asSecs()) {
|
||||
} else if (!isReaction && event.createdAt().asSecs() >= existingRoom.createdAt.asSecs()) {
|
||||
// Only update preview if message is newer (handles sync/late arrivals)
|
||||
rooms[roomId] = existingRoom.copy(
|
||||
lastMessage = event.content(),
|
||||
@@ -268,10 +290,10 @@ class ChatRepository(
|
||||
unreadCount = if (isFromMe) existingRoom.unreadCount else existingRoom.unreadCount + 1
|
||||
)
|
||||
} else if (isFromMe && existingRoom.kind != RoomKind.Ongoing) {
|
||||
// Even if it's an older message, if it's from me, the room is ongoing
|
||||
// Even if it's an older message or reaction, if it's from me, the room is ongoing
|
||||
rooms[roomId] = existingRoom.copy(kind = RoomKind.Ongoing)
|
||||
} else {
|
||||
// Don't update the room list state for older messages
|
||||
// Don't update the room list state for older messages or reactions that don't change preview
|
||||
return@update currentState
|
||||
}
|
||||
currentState.copy(rooms = rooms)
|
||||
|
||||
@@ -74,4 +74,8 @@ class ChatScreenViewModel(
|
||||
fun sendFileMessage(file: ByteArray?, type: String?) {
|
||||
chatRepository.sendFileMessage(id, file, type)
|
||||
}
|
||||
|
||||
fun sendReaction(targetEventId: EventId, reaction: String) {
|
||||
chatRepository.sendReaction(id, targetEventId, reaction)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user