chore: update nostr sdk
This commit is contained in:
@@ -24,7 +24,7 @@ kotlin {
|
|||||||
implementation(libs.jetbrains.navigation3.ui)
|
implementation(libs.jetbrains.navigation3.ui)
|
||||||
implementation(libs.jetbrains.lifecycle.viewmodelNavigation3)
|
implementation(libs.jetbrains.lifecycle.viewmodelNavigation3)
|
||||||
implementation(libs.androidx.core.splashscreen)
|
implementation(libs.androidx.core.splashscreen)
|
||||||
implementation("su.reya:nostr-sdk-kmp:0.2.3")
|
implementation("su.reya:nostr-sdk-kmp:0.2.6")
|
||||||
implementation("io.coil-kt.coil3:coil-compose:3.4.0")
|
implementation("io.coil-kt.coil3:coil-compose:3.4.0")
|
||||||
implementation("io.coil-kt.coil3:coil-network-okhttp:3.4.0")
|
implementation("io.coil-kt.coil3:coil-network-okhttp:3.4.0")
|
||||||
implementation("io.github.kalinjul.easyqrscan:scanner:0.7.0")
|
implementation("io.github.kalinjul.easyqrscan:scanner:0.7.0")
|
||||||
|
|||||||
@@ -58,7 +58,11 @@ class NostrForegroundService : Service() {
|
|||||||
dbDir.mkdirs()
|
dbDir.mkdirs()
|
||||||
|
|
||||||
// Initialize Nostr client
|
// Initialize Nostr client
|
||||||
nostr.init(dbDir.absolutePath)
|
try {
|
||||||
|
nostr.init(dbDir.absolutePath)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
throw IllegalStateException("Failed to initialize Nostr Client", e)
|
||||||
|
}
|
||||||
// Connect to bootstrap relays
|
// Connect to bootstrap relays
|
||||||
nostr.connectBootstrapRelays()
|
nostr.connectBootstrapRelays()
|
||||||
// Handle notifications
|
// Handle notifications
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ kotlin {
|
|||||||
implementation(libs.androidx.lifecycle.runtimeCompose)
|
implementation(libs.androidx.lifecycle.runtimeCompose)
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.8.0")
|
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.8.0")
|
||||||
implementation("su.reya:nostr-sdk-kmp:0.2.3")
|
implementation("su.reya:nostr-sdk-kmp:0.2.6")
|
||||||
implementation("com.squareup.okio:okio:3.16.2")
|
implementation("com.squareup.okio:okio:3.16.2")
|
||||||
}
|
}
|
||||||
androidMain.dependencies {
|
androidMain.dependencies {
|
||||||
|
|||||||
@@ -43,18 +43,18 @@ import rust.nostr.sdk.RelayUrl
|
|||||||
import rust.nostr.sdk.ReqExitPolicy
|
import rust.nostr.sdk.ReqExitPolicy
|
||||||
import rust.nostr.sdk.ReqTarget
|
import rust.nostr.sdk.ReqTarget
|
||||||
import rust.nostr.sdk.SendEventTarget
|
import rust.nostr.sdk.SendEventTarget
|
||||||
|
import rust.nostr.sdk.SignerAuthenticator
|
||||||
import rust.nostr.sdk.SingleLetterTag
|
import rust.nostr.sdk.SingleLetterTag
|
||||||
import rust.nostr.sdk.SleepWhenIdle
|
import rust.nostr.sdk.SleepWhenIdle
|
||||||
import rust.nostr.sdk.SubscribeAutoCloseOptions
|
import rust.nostr.sdk.SubscribeAutoCloseOptions
|
||||||
import rust.nostr.sdk.Tag
|
import rust.nostr.sdk.Tag
|
||||||
import rust.nostr.sdk.TagKind
|
|
||||||
import rust.nostr.sdk.Timestamp
|
import rust.nostr.sdk.Timestamp
|
||||||
import rust.nostr.sdk.UnsignedEvent
|
import rust.nostr.sdk.UnsignedEvent
|
||||||
import rust.nostr.sdk.UnwrappedGift
|
import rust.nostr.sdk.UnwrappedGift
|
||||||
import rust.nostr.sdk.extractRelayList
|
import rust.nostr.sdk.extractRelayList
|
||||||
import rust.nostr.sdk.giftWrapAsync
|
|
||||||
import rust.nostr.sdk.initLogger
|
import rust.nostr.sdk.initLogger
|
||||||
import rust.nostr.sdk.nip17ExtractRelayList
|
import rust.nostr.sdk.nip17ExtractRelayList
|
||||||
|
import rust.nostr.sdk.nip59MakeGiftWrapAsync
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
|
||||||
@@ -120,27 +120,23 @@ class Nostr {
|
|||||||
// Initialize the logger for nostr client
|
// Initialize the logger for nostr client
|
||||||
initLogger(logLevel)
|
initLogger(logLevel)
|
||||||
|
|
||||||
// Initialize the database and gossip instance
|
// Initialize configurations for nostr client
|
||||||
val lmdb = NostrDatabase.lmdb(dbPath)
|
val lmdb = NostrDatabase.lmdb(dbPath)
|
||||||
val gossip = NostrGossip.inMemory()
|
val gossip = NostrGossip.inMemory()
|
||||||
|
val authenticator = SignerAuthenticator(signer)
|
||||||
// Set the idle timeout for relays
|
|
||||||
val idleTimeout = Duration.parse("5m")
|
val idleTimeout = Duration.parse("5m")
|
||||||
|
|
||||||
client =
|
client =
|
||||||
ClientBuilder()
|
ClientBuilder()
|
||||||
.signer(signer)
|
.authenticator(authenticator)
|
||||||
.database(lmdb)
|
.database(lmdb)
|
||||||
.gossip(gossip)
|
.gossip(gossip)
|
||||||
.gossipConfig(
|
.gossipConfig(
|
||||||
GossipConfig()
|
GossipConfig()
|
||||||
.noBackgroundRefresh()
|
.noBackgroundRefresh()
|
||||||
.fetchTimeout(Duration.parse("2s"))
|
.fetchTimeout(Duration.parse("2s"))
|
||||||
.syncIdleTimeout(Duration.parse("100ms"))
|
|
||||||
.syncInitialTimeout(Duration.parse("100ms"))
|
|
||||||
)
|
)
|
||||||
.verifySubscriptions(false)
|
.verifySubscriptions(false)
|
||||||
.automaticAuthentication(true)
|
|
||||||
.sleepWhenIdle(SleepWhenIdle.Enabled(idleTimeout))
|
.sleepWhenIdle(SleepWhenIdle.Enabled(idleTimeout))
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
@@ -391,16 +387,15 @@ class Nostr {
|
|||||||
val currentUser =
|
val currentUser =
|
||||||
signer.currentUser ?: throw IllegalStateException("User not signed in")
|
signer.currentUser ?: throw IllegalStateException("User not signed in")
|
||||||
|
|
||||||
// Ensure the rumor ID is set
|
// Construct the room id
|
||||||
val rumor = rumor.ensureId()
|
|
||||||
val roomId = rumor.roomId()
|
val roomId = rumor.roomId()
|
||||||
|
|
||||||
// Construct reference tags
|
// Construct reference tags
|
||||||
val tags = listOf(
|
val tags = listOf(
|
||||||
Tag.identifier(giftId.toHex()),
|
Tag.identifier(giftId.toHex()),
|
||||||
Tag.event(rumor.id()!!),
|
Tag.event(rumor.id()!!),
|
||||||
Tag.reference(roomId.toString()),
|
Tag.custom("a", listOf(roomId.toString())),
|
||||||
Tag.custom(TagKind.Unknown("k"), listOf("14"))
|
Tag.custom("k", listOf("14"))
|
||||||
)
|
)
|
||||||
|
|
||||||
// Set event kind
|
// Set event kind
|
||||||
@@ -408,8 +403,8 @@ class Nostr {
|
|||||||
|
|
||||||
val event = EventBuilder(kind, rumor.asJson())
|
val event = EventBuilder(kind, rumor.asJson())
|
||||||
.tags(tags)
|
.tags(tags)
|
||||||
.build(currentUser)
|
.finalizeUnsigned(currentUser)
|
||||||
.signWithKeys(Keys.generate())
|
.signAsync(Keys.generate())
|
||||||
|
|
||||||
client?.database()?.saveEvent(event)
|
client?.database()?.saveEvent(event)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@@ -487,7 +482,7 @@ class Nostr {
|
|||||||
suspend fun createIdentity(keys: Keys, name: String, bio: String?, picture: String?) {
|
suspend fun createIdentity(keys: Keys, name: String, bio: String?, picture: String?) {
|
||||||
// Send relay list event
|
// Send relay list event
|
||||||
val relayList = getDefaultRelayList()
|
val relayList = getDefaultRelayList()
|
||||||
val relayListEvent = EventBuilder.relayList(relayList).signWithKeys(keys);
|
val relayListEvent = EventBuilder.relayList(relayList).finalizeAsync(keys);
|
||||||
|
|
||||||
client?.sendEvent(
|
client?.sendEvent(
|
||||||
event = relayListEvent,
|
event = relayListEvent,
|
||||||
@@ -498,7 +493,7 @@ class Nostr {
|
|||||||
|
|
||||||
// Send messaging relay list event
|
// Send messaging relay list event
|
||||||
val msgRelayList = getDefaultMsgRelayList()
|
val msgRelayList = getDefaultMsgRelayList()
|
||||||
val msgRelayListEvent = EventBuilder.nip17RelayList(msgRelayList).signWithKeys(keys)
|
val msgRelayListEvent = EventBuilder.nip17RelayList(msgRelayList).finalizeAsync(keys)
|
||||||
|
|
||||||
client?.sendEvent(
|
client?.sendEvent(
|
||||||
event = msgRelayListEvent,
|
event = msgRelayListEvent,
|
||||||
@@ -509,7 +504,7 @@ class Nostr {
|
|||||||
// Send metadata event
|
// Send metadata event
|
||||||
val metadata =
|
val metadata =
|
||||||
Metadata.fromRecord(MetadataRecord(displayName = name, about = bio, picture = picture))
|
Metadata.fromRecord(MetadataRecord(displayName = name, about = bio, picture = picture))
|
||||||
val metadataEvent = EventBuilder.metadata(metadata).signWithKeys(keys)
|
val metadataEvent = EventBuilder.metadata(metadata).finalizeAsync(keys)
|
||||||
|
|
||||||
client?.sendEvent(
|
client?.sendEvent(
|
||||||
event = metadataEvent,
|
event = metadataEvent,
|
||||||
@@ -519,8 +514,8 @@ class Nostr {
|
|||||||
|
|
||||||
// Send contact list event
|
// Send contact list event
|
||||||
val defaultContact =
|
val defaultContact =
|
||||||
listOf(Contact(publicKey = PublicKey.parse("npub1j3rz3ndl902lya6ywxvy5c983lxs8mpukqnx4pa4lt5wrykwl5ys7wpw3x")))
|
Contact(PublicKey.parse("npub1j3rz3ndl902lya6ywxvy5c983lxs8mpukqnx4pa4lt5wrykwl5ys7wpw3x"))
|
||||||
val contactListEvent = EventBuilder.contactList(defaultContact).signWithKeys(keys)
|
val contactListEvent = EventBuilder.contactList(listOf(defaultContact)).finalizeAsync(keys)
|
||||||
|
|
||||||
client?.sendEvent(
|
client?.sendEvent(
|
||||||
event = contactListEvent,
|
event = contactListEvent,
|
||||||
@@ -546,7 +541,7 @@ class Nostr {
|
|||||||
picture = picture ?: record.picture
|
picture = picture ?: record.picture
|
||||||
)
|
)
|
||||||
val newMetadata = Metadata.fromRecord(newRecord)
|
val newMetadata = Metadata.fromRecord(newRecord)
|
||||||
val event = EventBuilder.metadata(newMetadata).signAsync(signer)
|
val event = EventBuilder.metadata(newMetadata).finalizeAsync(signer)
|
||||||
|
|
||||||
client?.sendEvent(
|
client?.sendEvent(
|
||||||
event = event,
|
event = event,
|
||||||
@@ -623,7 +618,7 @@ class Nostr {
|
|||||||
|
|
||||||
suspend fun setMsgRelays(urls: List<RelayUrl>) {
|
suspend fun setMsgRelays(urls: List<RelayUrl>) {
|
||||||
try {
|
try {
|
||||||
val event = EventBuilder.nip17RelayList(urls).signAsync(signer)
|
val event = EventBuilder.nip17RelayList(urls).finalizeAsync(signer)
|
||||||
|
|
||||||
client?.sendEvent(
|
client?.sendEvent(
|
||||||
event = event,
|
event = event,
|
||||||
@@ -787,7 +782,7 @@ class Nostr {
|
|||||||
|
|
||||||
// Add a subject tag if provided
|
// Add a subject tag if provided
|
||||||
if (subject != null) {
|
if (subject != null) {
|
||||||
tags.add(Tag.custom(TagKind.Subject, listOf(subject)))
|
tags.add(Tag.custom("subject", listOf(subject)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add event tags for replies
|
// Add event tags for replies
|
||||||
@@ -805,13 +800,9 @@ class Nostr {
|
|||||||
for (receiver in setOf(currentUser) + to) {
|
for (receiver in setOf(currentUser) + to) {
|
||||||
// Construct the rumor event
|
// Construct the rumor event
|
||||||
// NEVER SIGN this event with the current user signer
|
// NEVER SIGN this event with the current user signer
|
||||||
val rumor = EventBuilder
|
val rumor = EventBuilder(Kind.fromStd(KindStandard.PRIVATE_DIRECT_MESSAGE), content)
|
||||||
.privateMsgRumor(receiver = receiver, message = content)
|
|
||||||
.tags(tags)
|
.tags(tags)
|
||||||
.allowSelfTagging()
|
.finalizeUnsigned(currentUser)
|
||||||
.build(currentUser)
|
|
||||||
// Ensure the event ID is set
|
|
||||||
.ensureId()
|
|
||||||
|
|
||||||
// Emit the rumor to the chat screen
|
// Emit the rumor to the chat screen
|
||||||
if (receiver == currentUser) {
|
if (receiver == currentUser) {
|
||||||
@@ -819,12 +810,12 @@ class Nostr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Construct the gift wrap event
|
// Construct the gift wrap event
|
||||||
val gift = giftWrapAsync(
|
val gift = nip59MakeGiftWrapAsync(
|
||||||
signer = signer,
|
signer = signer,
|
||||||
receiverPubkey = receiver,
|
receiverPubkey = receiver,
|
||||||
rumor = rumor,
|
rumor = rumor,
|
||||||
extraTags = listOf(
|
extraTags = listOf(
|
||||||
Tag.custom(TagKind.Unknown("k"), listOf("14"))
|
Tag.custom("k", listOf("14"))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ import rust.nostr.sdk.AsyncNostrSigner
|
|||||||
import rust.nostr.sdk.EventBuilder
|
import rust.nostr.sdk.EventBuilder
|
||||||
import rust.nostr.sdk.EventId
|
import rust.nostr.sdk.EventId
|
||||||
import rust.nostr.sdk.Keys
|
import rust.nostr.sdk.Keys
|
||||||
|
import rust.nostr.sdk.Kind
|
||||||
|
import rust.nostr.sdk.KindStandard
|
||||||
import rust.nostr.sdk.Metadata
|
import rust.nostr.sdk.Metadata
|
||||||
import rust.nostr.sdk.NostrConnect
|
import rust.nostr.sdk.NostrConnect
|
||||||
import rust.nostr.sdk.NostrConnectUri
|
import rust.nostr.sdk.NostrConnectUri
|
||||||
@@ -438,23 +440,20 @@ class NostrViewModel(
|
|||||||
contentType: String? = null
|
contentType: String? = null
|
||||||
) {
|
) {
|
||||||
_isLoggedIn.value = true
|
_isLoggedIn.value = true
|
||||||
try {
|
|
||||||
val keys = Keys.generate()
|
|
||||||
val secret = keys.secretKey().toBech32()
|
|
||||||
val avatarUrl = picture?.let { blossomUpload(it, contentType ?: "image/jpeg") }
|
|
||||||
|
|
||||||
|
val keys = Keys.generate()
|
||||||
|
val secret = keys.secretKey().toBech32()
|
||||||
|
|
||||||
|
try {
|
||||||
|
val avatarUrl = picture?.let { blossomUpload(it, contentType ?: "image/jpeg") }
|
||||||
// Create identity
|
// Create identity
|
||||||
nostr.createIdentity(keys = keys, name = name, bio, picture = avatarUrl)
|
nostr.createIdentity(keys = keys, name = name, bio, picture = avatarUrl)
|
||||||
|
|
||||||
// Save secret to the secret storage
|
|
||||||
secretStore.set("user_signer", secret)
|
|
||||||
|
|
||||||
// Set an empty secret state
|
|
||||||
_signerRequired.value = false
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
showError("Error: ${e.message}")
|
showError("Error: ${e.message}")
|
||||||
} finally {
|
} finally {
|
||||||
|
secretStore.set("user_signer", secret)
|
||||||
_isLoggedIn.value = false
|
_isLoggedIn.value = false
|
||||||
|
_signerRequired.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -476,10 +475,10 @@ class NostrViewModel(
|
|||||||
try {
|
try {
|
||||||
val signer = createSigner(secret)
|
val signer = createSigner(secret)
|
||||||
nostr.setSigner(signer)
|
nostr.setSigner(signer)
|
||||||
secretStore.set("user_signer", secret)
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
showError("Error: ${e.message}")
|
showError("Error: ${e.message}")
|
||||||
} finally {
|
} finally {
|
||||||
|
secretStore.set("user_signer", secret)
|
||||||
_signerRequired.value = false
|
_signerRequired.value = false
|
||||||
_isLoggedIn.value = false
|
_isLoggedIn.value = false
|
||||||
}
|
}
|
||||||
@@ -520,10 +519,9 @@ class NostrViewModel(
|
|||||||
val currentUser = nostr.signer.currentUser!!
|
val currentUser = nostr.signer.currentUser!!
|
||||||
|
|
||||||
// Construct the rumor event
|
// Construct the rumor event
|
||||||
val rumor = EventBuilder
|
val rumor = EventBuilder(Kind.fromStd(KindStandard.PRIVATE_DIRECT_MESSAGE), "")
|
||||||
.privateMsgRumor(to.first(), "")
|
|
||||||
.tags(to.map { Tag.publicKey(it) })
|
.tags(to.map { Tag.publicKey(it) })
|
||||||
.build(currentUser)
|
.finalizeUnsigned(currentUser)
|
||||||
|
|
||||||
// Check if the room already exists
|
// Check if the room already exists
|
||||||
val id = rumor.roomId()
|
val id = rumor.roomId()
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import kotlinx.datetime.minus
|
|||||||
import kotlinx.datetime.number
|
import kotlinx.datetime.number
|
||||||
import kotlinx.datetime.toLocalDateTime
|
import kotlinx.datetime.toLocalDateTime
|
||||||
import rust.nostr.sdk.PublicKey
|
import rust.nostr.sdk.PublicKey
|
||||||
import rust.nostr.sdk.TagKind
|
|
||||||
import rust.nostr.sdk.Timestamp
|
import rust.nostr.sdk.Timestamp
|
||||||
import rust.nostr.sdk.UnsignedEvent
|
import rust.nostr.sdk.UnsignedEvent
|
||||||
import kotlin.time.Clock
|
import kotlin.time.Clock
|
||||||
@@ -37,7 +36,7 @@ data class Room(
|
|||||||
fun new(rumor: UnsignedEvent, userPubkey: PublicKey): Room {
|
fun new(rumor: UnsignedEvent, userPubkey: PublicKey): Room {
|
||||||
val id = rumor.roomId()
|
val id = rumor.roomId()
|
||||||
val createdAt = rumor.createdAt()
|
val createdAt = rumor.createdAt()
|
||||||
val subject = rumor.tags().find(TagKind.Subject)?.content()
|
val subject = rumor.tags().toVec().find { it.kind() == "subject" }?.content()
|
||||||
|
|
||||||
// Collect the author's public key and all public keys from tags
|
// Collect the author's public key and all public keys from tags
|
||||||
val pubkeys: MutableSet<PublicKey> = mutableSetOf()
|
val pubkeys: MutableSet<PublicKey> = mutableSetOf()
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ class BlossomClient(
|
|||||||
signer: AsyncNostrSigner,
|
signer: AsyncNostrSigner,
|
||||||
authz: BlossomAuthorization
|
authz: BlossomAuthorization
|
||||||
): HeaderValue {
|
): HeaderValue {
|
||||||
val authEvent = EventBuilder.blossomAuth(authz).signAsync(signer)
|
val authEvent = EventBuilder.blossomAuth(authz).finalizeAsync(signer)
|
||||||
val encodedAuth = Base64.encode(authEvent.asJson().toByteArray())
|
val encodedAuth = Base64.encode(authEvent.asJson().toByteArray())
|
||||||
val value = "Nostr $encodedAuth"
|
val value = "Nostr $encodedAuth"
|
||||||
return HeaderValue(value)
|
return HeaderValue(value)
|
||||||
|
|||||||
Reference in New Issue
Block a user