- Removes compose bom from Quartz to avoid unnecessary dependencies.
- Removes old datetime dependencies from Quartz - Moves compose.runtime dependencies to compose.runtime.annotation - Adds dependency on coroutines directly (instead of through compose runtime) - Removes old secp256 target dependencies - Adds Default scope for NostrClient and Relay Authenticator - Updates readme
This commit is contained in:
@@ -239,6 +239,15 @@ openssl base64 < <my-release-key.keystore> | tr -d '\n' | tee some_signing_key.j
|
||||
|
||||
### Installing
|
||||
|
||||
Add Maven Central and Google Maven to your repositories:
|
||||
|
||||
```gradle
|
||||
repositories {
|
||||
mavenCentral()
|
||||
google()
|
||||
}
|
||||
```
|
||||
|
||||
Add the following line to your `commonMain` dependencies:
|
||||
|
||||
```gradle
|
||||
@@ -283,8 +292,8 @@ val amberSigner = NostrSignerExternal(
|
||||
)
|
||||
```
|
||||
|
||||
Create a single NostrClient for the entire application and control which relays it will access by
|
||||
registering subscriptions and sending events. The pool will automatically changed based on filters +
|
||||
Create a single `NostrClient` for the entire application and control which relays it will access by
|
||||
registering subscriptions and sending events. The pool will automatically change based on filters +
|
||||
outbox events.
|
||||
|
||||
You will need a coroutine scope to process events and if you are using OKHttp, we offer a basic
|
||||
@@ -301,9 +310,11 @@ val client = NostrClient(socketBuilder, appScope)
|
||||
If you want to auth, given a logged-in `signer`:
|
||||
|
||||
```kt
|
||||
val authCoordinator = RelayAuthenticator(client, appScope) { challenge, relay ->
|
||||
val authedEvent = RelayAuthEvent.create(relay.url, challenge, signer)
|
||||
client.sendIfExists(authedEvent, relay.url)
|
||||
val authCoordinator = RelayAuthenticator(client, appScope) { authTemplate ->
|
||||
listOf(
|
||||
// for each signed-in user, return an event
|
||||
signer.sign(authTemplate)
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
@@ -311,8 +322,8 @@ To make a request subscription simply do:
|
||||
|
||||
```kt
|
||||
val metadataSub = client.req(
|
||||
relays = listOf("wss://nos.lol", "wss://nostr.mom"),
|
||||
filters = Filter(
|
||||
relay = "wss://nos.lol",
|
||||
filter = Filter(
|
||||
kinds = listOf(MetadataEvent.KIND),
|
||||
authors = listOf(signer.pubkey)
|
||||
)
|
||||
|
||||
@@ -53,11 +53,8 @@ dependencies {
|
||||
// Lru Cache used in Relay stats
|
||||
implementation libs.androidx.core.ktx
|
||||
|
||||
// Compose's @Immutable and @Stable classes
|
||||
implementation libs.androidx.compose.runtime
|
||||
implementation project(path: ':quartz')
|
||||
|
||||
|
||||
testImplementation libs.junit
|
||||
|
||||
androidTestImplementation libs.androidx.junit
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
*/
|
||||
package com.vitorpamplona.ammolite.relays
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.vitorpamplona.ammolite.service.checkNotInMainThread
|
||||
import com.vitorpamplona.quartz.utils.Log
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
@@ -37,7 +36,6 @@ import java.util.concurrent.LinkedBlockingQueue
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
/** This class is designed to have a waiting time between two calls of invalidate */
|
||||
@Stable
|
||||
class BundledUpdate(
|
||||
val delay: Long,
|
||||
val dispatcher: CoroutineDispatcher = Dispatchers.IO,
|
||||
@@ -63,7 +61,6 @@ class BundledUpdate(
|
||||
}
|
||||
|
||||
/** This class is designed to have a waiting time between two calls of invalidate */
|
||||
@Stable
|
||||
class BasicBundledUpdate(
|
||||
val delay: Long,
|
||||
val dispatcher: CoroutineDispatcher = Dispatchers.IO,
|
||||
@@ -101,7 +98,6 @@ class BasicBundledUpdate(
|
||||
}
|
||||
|
||||
/** This class is designed to have a waiting time between two calls of invalidate */
|
||||
@Stable
|
||||
class BundledInsert<T>(
|
||||
val delay: Long,
|
||||
val dispatcher: CoroutineDispatcher = Dispatchers.IO,
|
||||
@@ -127,7 +123,6 @@ class BundledInsert<T>(
|
||||
}
|
||||
|
||||
/** This class is designed to have a waiting time between two calls of invalidate */
|
||||
@Stable
|
||||
class BasicBundledInsert<T>(
|
||||
val delay: Long,
|
||||
val dispatcher: CoroutineDispatcher = Dispatchers.IO,
|
||||
|
||||
@@ -14,6 +14,7 @@ benchmarkJunit4 = "1.4.1"
|
||||
biometricKtx = "1.2.0-alpha05"
|
||||
coil = "3.3.0"
|
||||
composeBom = "2025.12.00"
|
||||
composeRuntimeAnnotation = "1.10.0"
|
||||
coreKtx = "1.17.0"
|
||||
datastore = "1.2.0"
|
||||
espressoCore = "3.7.0"
|
||||
@@ -26,7 +27,7 @@ jtorctl = "0.4.5.7"
|
||||
junit = "4.13.2"
|
||||
kotlin = "2.2.21"
|
||||
kotlinxCollectionsImmutable = "0.4.0"
|
||||
kotlinxDatetime = "0.7.1"
|
||||
kotlinxCoroutinesCore = "1.10.2"
|
||||
kotlinxSerialization = "1.9.0"
|
||||
kotlinxSerializationPlugin = "2.2.21"
|
||||
languageId = "17.0.6"
|
||||
@@ -77,8 +78,8 @@ androidx-camera-view = { module = "androidx.camera:camera-view", version.ref = "
|
||||
androidx-camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "androidxCamera" }
|
||||
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
|
||||
androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation" }
|
||||
androidx-compose-runtime = { group = "androidx.compose.runtime", name = "runtime" }
|
||||
androidx-collection-jvm = { group = "androidx.collection", name = "collection-jvm", version.ref = "androidxCollection" }
|
||||
androidx-compose-runtime-annotation = { group = "androidx.compose.runtime", name = "runtime-annotation", version.ref = "composeRuntimeAnnotation" }
|
||||
androidx-collection = { group = "androidx.collection", name = "collection", version.ref = "androidxCollection" }
|
||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||
androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastore" }
|
||||
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
|
||||
@@ -120,7 +121,7 @@ jna = { group = "net.java.dev.jna", name = "jna", version.ref = "jna" }
|
||||
jtorctl = { module = "info.guardianproject:jtorctl", version.ref = "jtorctl" }
|
||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||
kotlinx-collections-immutable = { group = "org.jetbrains.kotlinx", name = "kotlinx-collections-immutable", version.ref = "kotlinxCollectionsImmutable" }
|
||||
kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinxDatetime" }
|
||||
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutinesCore" }
|
||||
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerialization" }
|
||||
kotlinx-serialization-cbor = { module = "org.jetbrains.kotlinx:kotlinx-serialization-cbor", version.ref = "kotlinxSerialization" }
|
||||
lazysodium-java = { group = "com.goterl", name = "lazysodium-java", version.ref = "lazysodiumJava" }
|
||||
@@ -137,9 +138,6 @@ rfc3986-normalizer = { group = "org.czeal", name = "rfc3986", version.ref = "rfc
|
||||
secp256k1-kmp-common = { group = "fr.acinq.secp256k1", name = "secp256k1-kmp", version.ref = "secp256k1KmpJniAndroid" }
|
||||
secp256k1-kmp-jni-android = { group = "fr.acinq.secp256k1", name = "secp256k1-kmp-jni-android", version.ref = "secp256k1KmpJniAndroid" }
|
||||
secp256k1-kmp-jni-jvm = { group = "fr.acinq.secp256k1", name = "secp256k1-kmp-jni-jvm", version.ref = "secp256k1KmpJniAndroid" }
|
||||
secp256k1-kmp-jni-jvm-linux = { group = "fr.acinq.secp256k1", name = "secp256k1-kmp-jni-jvm-linux", version.ref = "secp256k1KmpJniAndroid" }
|
||||
secp256k1-kmp-jni-jvm-darwin = { group = "fr.acinq.secp256k1", name = "secp256k1-kmp-jni-jvm-darwin", version.ref = "secp256k1KmpJniAndroid" }
|
||||
secp256k1-kmp-jni-jvm-mingw = { group = "fr.acinq.secp256k1", name = "secp256k1-kmp-jni-jvm-mingw", version.ref = "secp256k1KmpJniAndroid" }
|
||||
tor-android = { module = "info.guardianproject:tor-android", version.ref = "torAndroid" }
|
||||
unifiedpush = { group = "com.github.UnifiedPush", name = "android-connector", version.ref = "unifiedpush" }
|
||||
url-detector = { group = "io.github.url-detector", name = "url-detector", version.ref = "urlDetector" }
|
||||
|
||||
+6
-12
@@ -95,10 +95,13 @@ kotlin {
|
||||
commonMain {
|
||||
dependencies {
|
||||
implementation(libs.kotlin.stdlib)
|
||||
implementation(project.dependencies.platform(libs.androidx.compose.bom))
|
||||
implementation(libs.kotlinx.coroutines.core)
|
||||
|
||||
// For LruCache
|
||||
implementation(libs.androidx.collection)
|
||||
|
||||
// @Immutable and @Stable
|
||||
implementation(libs.androidx.compose.runtime)
|
||||
implementation(libs.androidx.compose.runtime.annotation)
|
||||
|
||||
// Bitcoin secp256k1 bindings
|
||||
api(libs.secp256k1.kmp.common)
|
||||
@@ -106,9 +109,6 @@ kotlin {
|
||||
// Kotlin serialization for the times where we need the Json tree and performance is not that important.
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
|
||||
// in your shared module's dependencies block
|
||||
// implementation( libs.kotlinx.datetime)
|
||||
|
||||
// immutable collections to avoid recomposition
|
||||
implementation(libs.kotlinx.collections.immutable)
|
||||
}
|
||||
@@ -126,9 +126,6 @@ kotlin {
|
||||
dependsOn(commonMain.get())
|
||||
|
||||
dependencies {
|
||||
// For LruCache
|
||||
implementation(libs.androidx.collection.jvm)
|
||||
|
||||
// Normalizes URLs
|
||||
api(libs.rfc3986.normalizer)
|
||||
|
||||
@@ -157,7 +154,7 @@ kotlin {
|
||||
dependsOn(jvmAndroid)
|
||||
dependencies {
|
||||
// Bitcoin secp256k1 bindings
|
||||
api(libs.secp256k1.kmp.jni.jvm)
|
||||
implementation (libs.secp256k1.kmp.jni.jvm)
|
||||
|
||||
// LibSodium for ChaCha encryption (NIP-44)
|
||||
implementation (libs.lazysodium.java)
|
||||
@@ -178,9 +175,6 @@ kotlin {
|
||||
dependencies {
|
||||
implementation(libs.androidx.core.ktx)
|
||||
|
||||
// @Immutable and @Stable
|
||||
implementation(libs.androidx.compose.runtime)
|
||||
|
||||
// Bitcoin secp256k1 bindings to Android
|
||||
api(libs.secp256k1.kmp.jni.android)
|
||||
|
||||
|
||||
+4
-1
@@ -37,7 +37,10 @@ import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.sockets.WebsocketBuilder
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.IO
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.combine
|
||||
@@ -74,7 +77,7 @@ import kotlinx.coroutines.launch
|
||||
*/
|
||||
class NostrClient(
|
||||
private val websocketBuilder: WebsocketBuilder,
|
||||
private val scope: CoroutineScope,
|
||||
private val scope: CoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob()),
|
||||
) : INostrClient,
|
||||
IRelayClientListener {
|
||||
private val relayPool: RelayPool = RelayPool(websocketBuilder, this)
|
||||
|
||||
+4
-1
@@ -32,6 +32,9 @@ import com.vitorpamplona.quartz.nip01Core.signers.EventTemplate
|
||||
import com.vitorpamplona.quartz.nip42RelayAuth.RelayAuthEvent
|
||||
import com.vitorpamplona.quartz.utils.Log
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.IO
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
interface IAuthStatus {
|
||||
@@ -44,7 +47,7 @@ object EmptyIAuthStatus : IAuthStatus {
|
||||
|
||||
class RelayAuthenticator(
|
||||
val client: INostrClient,
|
||||
val scope: CoroutineScope,
|
||||
val scope: CoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob()),
|
||||
val signWithAllLoggedInUsers: suspend (EventTemplate<RelayAuthEvent>) -> List<RelayAuthEvent>,
|
||||
) : IAuthStatus {
|
||||
private val authStatus = mutableMapOf<NormalizedRelayUrl, RelayAuthStatus>()
|
||||
|
||||
+4
@@ -28,6 +28,8 @@ import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.utils.Log
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
@@ -43,6 +45,7 @@ class NostrClientSubscriptionAsFlowTest : BaseNostrClientTest() {
|
||||
return joinToString { (it.createdAt - starting).toString() }
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Test
|
||||
fun testNostrClientSubscriptionAsFlow() =
|
||||
runTest {
|
||||
@@ -81,6 +84,7 @@ class NostrClientSubscriptionAsFlowTest : BaseNostrClientTest() {
|
||||
assertEquals(10, feedStates.size)
|
||||
}
|
||||
|
||||
@OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class)
|
||||
@Test
|
||||
fun testNostrClientSubscriptionAsFlowDebouncing() =
|
||||
runTest {
|
||||
|
||||
Reference in New Issue
Block a user