From 8e7a1e6d7c230327cc911c00ca64f7aa588d8874 Mon Sep 17 00:00:00 2001 From: Ren Amamiya Date: Fri, 12 Jun 2026 10:21:56 +0700 Subject: [PATCH 1/4] fix --- .../su/reya/coop/AndroidExternalSigner.kt | 2 +- .../su/reya/coop/ExternalSignerLauncher.kt | 19 +++++++++----- .../commonMain/kotlin/su/reya/coop/Nostr.kt | 25 +++++++------------ 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/composeApp/src/androidMain/kotlin/su/reya/coop/AndroidExternalSigner.kt b/composeApp/src/androidMain/kotlin/su/reya/coop/AndroidExternalSigner.kt index e7e3122..3f367fe 100644 --- a/composeApp/src/androidMain/kotlin/su/reya/coop/AndroidExternalSigner.kt +++ b/composeApp/src/androidMain/kotlin/su/reya/coop/AndroidExternalSigner.kt @@ -61,7 +61,7 @@ class AndroidExternalSigner( ): String? { // Try Content Resolver first queryContentResolver(type, payload, pubkey, currentUser)?.let { - return it.result + return if (resultKey == "event") it.event else it.result } // Fall back to Intent diff --git a/composeApp/src/androidMain/kotlin/su/reya/coop/ExternalSignerLauncher.kt b/composeApp/src/androidMain/kotlin/su/reya/coop/ExternalSignerLauncher.kt index cbfb686..51bbba7 100644 --- a/composeApp/src/androidMain/kotlin/su/reya/coop/ExternalSignerLauncher.kt +++ b/composeApp/src/androidMain/kotlin/su/reya/coop/ExternalSignerLauncher.kt @@ -4,23 +4,30 @@ import android.content.Intent import androidx.activity.result.ActivityResult import androidx.activity.result.ActivityResultLauncher import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import kotlinx.coroutines.withContext class ExternalSignerLauncher { private var launcher: ActivityResultLauncher? = null private var pendingResult: CompletableDeferred? = null + private val mutex = Mutex() fun register(launcher: ActivityResultLauncher) { this.launcher = launcher } - suspend fun launch(intent: Intent): ActivityResult { - val deferred = CompletableDeferred() - pendingResult = deferred - launcher?.launch(intent) - ?: throw IllegalStateException("ExternalSignerLauncher not registered") - return deferred.await() + suspend fun launch(intent: Intent): ActivityResult = mutex.withLock { + withContext(Dispatchers.Main) { + val deferred = CompletableDeferred() + pendingResult = deferred + launcher?.launch(intent) ?: throw IllegalStateException("Signer not registered") + deferred.await() + } } + fun onResult(result: ActivityResult) { pendingResult?.complete(result) pendingResult = null diff --git a/shared/src/commonMain/kotlin/su/reya/coop/Nostr.kt b/shared/src/commonMain/kotlin/su/reya/coop/Nostr.kt index 7311152..f52ee6c 100644 --- a/shared/src/commonMain/kotlin/su/reya/coop/Nostr.kt +++ b/shared/src/commonMain/kotlin/su/reya/coop/Nostr.kt @@ -412,23 +412,16 @@ class Nostr { val cachedRumor = getCachedRumor(event.id()) if (cachedRumor != null) return cachedRumor - // Get all signers - val signers = listOfNotNull(signer, deviceSigner) - if (signers.isEmpty()) return null - // Try to unwrap the gift with each signer - for (signer in signers) { - try { - val gift = UnwrappedGift.fromGiftWrapAsync(signer = signer, giftWrap = event) - val rumor = gift.rumor() - // Save the rumor to the database - setCachedRumor(event.id(), rumor) - // Return the rumor - return rumor - } catch (e: Exception) { - println("Failed to unwrap gift: ${e.message}") - continue - } + try { + val gift = UnwrappedGift.fromGiftWrapAsync(signer = signer, giftWrap = event) + val rumor = gift.rumor() + // Save the rumor to the database + setCachedRumor(event.id(), rumor) + // Return the rumor + return rumor + } catch (e: Exception) { + println("Failed to unwrap gift: ${e.message}") } return null -- 2.49.1 From 44492425811f25c300f6e9ae7d09884a2b676b14 Mon Sep 17 00:00:00 2001 From: Ren Amamiya Date: Fri, 12 Jun 2026 10:43:42 +0700 Subject: [PATCH 2/4] fix crash when save event --- shared/src/commonMain/kotlin/su/reya/coop/Nostr.kt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/shared/src/commonMain/kotlin/su/reya/coop/Nostr.kt b/shared/src/commonMain/kotlin/su/reya/coop/Nostr.kt index f52ee6c..4f25795 100644 --- a/shared/src/commonMain/kotlin/su/reya/coop/Nostr.kt +++ b/shared/src/commonMain/kotlin/su/reya/coop/Nostr.kt @@ -379,9 +379,6 @@ class Nostr { private suspend fun setCachedRumor(giftId: EventId, rumor: UnsignedEvent) { try { - val currentUser = - signer.currentUser ?: throw IllegalStateException("User not signed in") - // Construct the room id val roomId = rumor.roomId() @@ -396,10 +393,10 @@ class Nostr { // Set event kind val kind = Kind.fromStd(KindStandard.APPLICATION_SPECIFIC_DATA); + // Construct event val event = EventBuilder(kind, rumor.asJson()) .tags(tags) - .finalizeUnsigned(currentUser) - .signAsync(Keys.generate()) + .finalizeAsync(signer) client?.database()?.saveEvent(event) } catch (e: Exception) { -- 2.49.1 From 5142e2cbc10cc3062d622fbd117d3add18216980 Mon Sep 17 00:00:00 2001 From: Ren Amamiya Date: Fri, 12 Jun 2026 14:11:22 +0700 Subject: [PATCH 3/4] fix empty messages --- .../androidMain/kotlin/su/reya/coop/screens/ChatScreen.kt | 8 ++++---- shared/src/commonMain/kotlin/su/reya/coop/Nostr.kt | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/composeApp/src/androidMain/kotlin/su/reya/coop/screens/ChatScreen.kt b/composeApp/src/androidMain/kotlin/su/reya/coop/screens/ChatScreen.kt index 6c306eb..3e4694f 100644 --- a/composeApp/src/androidMain/kotlin/su/reya/coop/screens/ChatScreen.kt +++ b/composeApp/src/androidMain/kotlin/su/reya/coop/screens/ChatScreen.kt @@ -110,17 +110,17 @@ fun ChatScreen(id: Long) { // Start loading spinner loading = true - // Get msg relays for each member - viewModel.chatRoomConnect(id) - // Get messages val initialMessages = viewModel.getChatRoomMessages(id) messages.clear() messages.addAll(initialMessages) - + // Stop loading spinner loading = false + // Get msg relays for each member + viewModel.chatRoomConnect(id) + // Handle new messages viewModel.newEvents.collect { event -> if (event.roomId() == id) { diff --git a/shared/src/commonMain/kotlin/su/reya/coop/Nostr.kt b/shared/src/commonMain/kotlin/su/reya/coop/Nostr.kt index 4f25795..193e135 100644 --- a/shared/src/commonMain/kotlin/su/reya/coop/Nostr.kt +++ b/shared/src/commonMain/kotlin/su/reya/coop/Nostr.kt @@ -386,7 +386,7 @@ class Nostr { val tags = listOf( Tag.identifier(giftId.toHex()), Tag.event(rumor.id()!!), - Tag.custom("a", listOf(roomId.toString())), + Tag.custom("r", listOf(roomId.toString())), Tag.custom("k", listOf("14")) ) @@ -396,7 +396,7 @@ class Nostr { // Construct event val event = EventBuilder(kind, rumor.asJson()) .tags(tags) - .finalizeAsync(signer) + .finalizeAsync(Keys.generate()) client?.database()?.saveEvent(event) } catch (e: Exception) { -- 2.49.1 From 893e579923220e54d30fda3960422c828e437afb Mon Sep 17 00:00:00 2001 From: Ren Amamiya Date: Fri, 12 Jun 2026 15:47:40 +0700 Subject: [PATCH 4/4] fix crash on send message --- composeApp/build.gradle.kts | 2 +- shared/build.gradle.kts | 2 +- .../commonMain/kotlin/su/reya/coop/Nostr.kt | 27 ++++++++++++------- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 52b3eed..3403d27 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -24,7 +24,7 @@ kotlin { implementation(libs.jetbrains.navigation3.ui) implementation(libs.jetbrains.lifecycle.viewmodelNavigation3) implementation(libs.androidx.core.splashscreen) - implementation("su.reya:nostr-sdk-kmp:0.2.6") + implementation("su.reya:nostr-sdk-kmp:0.2.7") implementation("io.coil-kt.coil3:coil-compose:3.4.0") implementation("io.coil-kt.coil3:coil-network-okhttp:3.4.0") implementation("io.github.kalinjul.easyqrscan:scanner:0.7.0") diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index 8a5b486..f8e160e 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -33,7 +33,7 @@ kotlin { implementation(libs.androidx.lifecycle.runtimeCompose) implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2") implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.8.0") - implementation("su.reya:nostr-sdk-kmp:0.2.6") + implementation("su.reya:nostr-sdk-kmp:0.2.7") implementation("com.squareup.okio:okio:3.16.2") } androidMain.dependencies { diff --git a/shared/src/commonMain/kotlin/su/reya/coop/Nostr.kt b/shared/src/commonMain/kotlin/su/reya/coop/Nostr.kt index 193e135..c029d97 100644 --- a/shared/src/commonMain/kotlin/su/reya/coop/Nostr.kt +++ b/shared/src/commonMain/kotlin/su/reya/coop/Nostr.kt @@ -385,6 +385,7 @@ class Nostr { // Construct reference tags val tags = listOf( Tag.identifier(giftId.toHex()), + Tag.publicKey(rumor.author()), Tag.event(rumor.id()!!), Tag.custom("r", listOf(roomId.toString())), Tag.custom("k", listOf("14")) @@ -405,16 +406,18 @@ class Nostr { } private suspend fun extractRumor(event: Event): UnsignedEvent? { - // Check if the rumor is already cached - val cachedRumor = getCachedRumor(event.id()) - if (cachedRumor != null) return cachedRumor - - // Try to unwrap the gift with each signer try { + // Check if the rumor is already cached + val cachedRumor = getCachedRumor(event.id()) + if (cachedRumor != null) return cachedRumor + + // Unwrap the gift with current signer val gift = UnwrappedGift.fromGiftWrapAsync(signer = signer, giftWrap = event) val rumor = gift.rumor() + // Save the rumor to the database setCachedRumor(event.id(), rumor) + // Return the rumor return rumor } catch (e: Exception) { @@ -676,12 +679,14 @@ class Nostr { suspend fun getChatRooms(): Set? { try { - val userPubkey = signer.currentUser ?: throw IllegalStateException("User not signed in") + val userPubkey = + signer.getPublicKeyAsync() ?: throw IllegalStateException("User not signed in") + val kind = Kind.fromStd(KindStandard.APPLICATION_SPECIFIC_DATA) val kTag = SingleLetterTag.lowercase(Alphabet.K) // Get all events sent by the user - val filter = Filter().kind(kind).author(userPubkey).customTags(kTag, listOf("14", "dm")) + val filter = Filter().kind(kind).pubkey(userPubkey).customTags(kTag, listOf("14", "dm")) val events = client?.database()?.query(filter) // Collect rooms @@ -697,8 +702,9 @@ class Nostr { // Check if the room already exists if (existingRoom == null || newRoom.createdAt.asSecs() > existingRoom.createdAt.asSecs()) { - val filter = - Filter().kind(kind).author(userPubkey).pubkeys(newRoom.members.toList()) + val kind = Kind.fromStd(KindStandard.PRIVATE_DIRECT_MESSAGE) + val pubkeys = newRoom.members.toList() + val filter = Filter().kind(kind).author(userPubkey).pubkeys(pubkeys) // Determine if it's an ongoing room val isOngoing = client?.database()?.query(filter)?.isEmpty() == false @@ -779,7 +785,7 @@ class Nostr { ) { try { val currentUser = - signer.currentUser ?: throw IllegalStateException("User not signed in") + signer.getPublicKeyAsync() ?: throw IllegalStateException("User not signed in") val tags = mutableListOf() @@ -806,6 +812,7 @@ class Nostr { val rumor = EventBuilder(Kind.fromStd(KindStandard.PRIVATE_DIRECT_MESSAGE), content) .tags(tags) .finalizeUnsigned(currentUser) + .ensureId() // Emit the rumor to the chat screen if (receiver == currentUser) { -- 2.49.1