ensure relay disconnect on background

This commit is contained in:
2026-06-06 15:46:13 +07:00
parent 6bd4c53445
commit 550fd3e527
2 changed files with 65 additions and 16 deletions

View File

@@ -38,6 +38,7 @@ import rust.nostr.sdk.PublicKey
import rust.nostr.sdk.RelayCapabilities import rust.nostr.sdk.RelayCapabilities
import rust.nostr.sdk.RelayMessageEnum import rust.nostr.sdk.RelayMessageEnum
import rust.nostr.sdk.RelayMetadata import rust.nostr.sdk.RelayMetadata
import rust.nostr.sdk.RelayStatus
import rust.nostr.sdk.RelayUrl 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
@@ -59,6 +60,18 @@ import kotlin.time.Duration.Companion.milliseconds
object NostrManager { object NostrManager {
val instance = Nostr() val instance = Nostr()
val BOOTSTRAP_RELAYS = listOf(
"wss://relay.primal.net",
"wss://user.kindpag.es",
"wss://purplepag.es"
)
val INDEXER_RELAY = listOf(
"wss://indexer.coracle.social",
)
val ALL_RELAYS = BOOTSTRAP_RELAYS + INDEXER_RELAY
} }
class Nostr { class Nostr {
@@ -75,7 +88,6 @@ class Nostr {
private val isInitialized = MutableStateFlow(false) private val isInitialized = MutableStateFlow(false)
// Add these to the Nostr class
private val _newEvents = MutableSharedFlow<UnsignedEvent>(extraBufferCapacity = 100) private val _newEvents = MutableSharedFlow<UnsignedEvent>(extraBufferCapacity = 100)
val newEvents = _newEvents.asSharedFlow() val newEvents = _newEvents.asSharedFlow()
@@ -144,24 +156,43 @@ class Nostr {
} }
suspend fun connectBootstrapRelays() { suspend fun connectBootstrapRelays() {
// Bootstrap relays NostrManager.BOOTSTRAP_RELAYS.forEach { url ->
client?.addRelay(RelayUrl.parse("wss://relay.primal.net")) client?.addRelay(RelayUrl.parse(url))
client?.addRelay(RelayUrl.parse("wss://user.kindpag.es")) }
client?.addRelay(RelayUrl.parse("wss://purplepag.es")) NostrManager.INDEXER_RELAY.forEach { url ->
client?.addRelay(
url = RelayUrl.parse(url),
capabilities = RelayCapabilities.gossip()
)
}
// Connect to all bootstrap relays
client?.connect()
}
suspend fun reconnect() {
// Indexer relay for NIP-65 discovery NostrManager.ALL_RELAYS.forEach { url ->
client?.addRelay( try {
url = RelayUrl.parse("wss://indexer.coracle.social"), client?.relay(RelayUrl.parse(url)).let { relay ->
capabilities = RelayCapabilities.gossip() if (relay != null) {
) if (relay.status() != RelayStatus.CONNECTED) {
relay.connect()
// Connect to all bootstrap relays and wait for all connections to be established }
client?.connect(Duration.parse("2s")) }
}
} catch (e: Exception) {
println("Failed to reconnect relay: ${e.message}")
}
}
} }
suspend fun disconnect() { suspend fun disconnect() {
client?.shutdown() NostrManager.ALL_RELAYS.forEach { url ->
try {
client?.disconnectRelay(RelayUrl.parse(url))
} catch (e: Exception) {
println("Failed to disconnect relay: ${e.message}")
}
}
} }
suspend fun exit() { suspend fun exit() {

View File

@@ -5,6 +5,7 @@ import androidx.lifecycle.viewModelScope
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.serialization.kotlinx.json.json import io.ktor.serialization.kotlinx.json.json
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
@@ -15,6 +16,7 @@ import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeoutOrNull import kotlinx.coroutines.withTimeoutOrNull
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import rust.nostr.sdk.AsyncNostrSigner import rust.nostr.sdk.AsyncNostrSigner
@@ -85,6 +87,9 @@ class NostrViewModel(
// Check local stored secret (secret key or bunker) // Check local stored secret (secret key or bunker)
login() login()
// Automatically reconnect bootstrap relays
reconnect()
// Observe the signer state and verify the relay list // Observe the signer state and verify the relay list
observeSignerAndCheckRelays() observeSignerAndCheckRelays()
@@ -100,7 +105,13 @@ class NostrViewModel(
override fun onCleared() { override fun onCleared() {
super.onCleared() super.onCleared()
// TODO: optimize relay connection
// Disconnect to all bootstrap relays
viewModelScope.launch {
withContext(NonCancellable) {
nostr.disconnect()
}
}
} }
private fun showError(message: String) { private fun showError(message: String) {
@@ -116,6 +127,13 @@ class NostrViewModel(
} }
} }
private fun reconnect() {
viewModelScope.launch {
nostr.waitUntilInitialized()
nostr.reconnect()
}
}
private fun runObserver() { private fun runObserver() {
viewModelScope.launch { viewModelScope.launch {
// Observe new messages // Observe new messages