additional info
This commit is contained in:
@@ -205,3 +205,96 @@ private fun NoteRow(handle: MutableStateFlow<TextNoteEvent>) {
|
||||
|
||||
Notice how each how also subscribe for changes. This is important to receive
|
||||
updates from replaceable and addressable events.
|
||||
|
||||
# Appendix A
|
||||
|
||||
Quartz doesn't offer a Ktor websocket, but you can use this one as reference.
|
||||
|
||||
```kotlin
|
||||
/**
|
||||
* Ktor-based [WebSocket] for talking to a Nostr relay.
|
||||
*
|
||||
* Quartz exposes [WebsocketBuilder] as the only seam between its relay-pool
|
||||
* and the underlying transport, so all this class has to do is open a Ktor
|
||||
* websocket session, forward incoming text frames to [out], and let Quartz
|
||||
* drive sends.
|
||||
*/
|
||||
class KtorWebSocket(
|
||||
private val url: NormalizedRelayUrl,
|
||||
private val httpClient: HttpClient,
|
||||
private val out: WebSocketListener,
|
||||
) : WebSocket {
|
||||
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||
private var session: DefaultWebSocketSession? = null
|
||||
private var readerJob: Job? = null
|
||||
|
||||
override fun needsReconnect(): Boolean = session == null
|
||||
|
||||
override fun connect() {
|
||||
readerJob =
|
||||
scope.launch {
|
||||
try {
|
||||
val s = httpClient.webSocketSession(urlString = url.url)
|
||||
session = s
|
||||
out.onOpen(0, false)
|
||||
|
||||
for (frame in s.incoming) {
|
||||
if (frame is Frame.Text) {
|
||||
out.onMessage(frame.readText())
|
||||
}
|
||||
}
|
||||
|
||||
val reason = s.closeReason.await()
|
||||
out.onClosed(
|
||||
code =
|
||||
reason?.code?.toInt() ?: CloseReason.Codes.NORMAL.code
|
||||
.toInt(),
|
||||
reason = reason?.message ?: "",
|
||||
)
|
||||
} catch (t: Throwable) {
|
||||
out.onFailure(t, null, null)
|
||||
} finally {
|
||||
session = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun disconnect() {
|
||||
val s = session
|
||||
session = null
|
||||
readerJob?.cancel()
|
||||
readerJob = null
|
||||
if (s != null) {
|
||||
runBlocking { s.close(CloseReason(CloseReason.Codes.NORMAL, "client disconnect")) }
|
||||
}
|
||||
scope.cancel()
|
||||
}
|
||||
|
||||
override fun send(msg: String): Boolean {
|
||||
val s = session ?: return false
|
||||
scope.launch { s.send(msg) }
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* The factory Quartz hands to [com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient].
|
||||
* One [HttpClient] is shared by every relay in the pool.
|
||||
*/
|
||||
class Builder(
|
||||
private val httpClient: HttpClient = defaultClient(),
|
||||
) : WebsocketBuilder {
|
||||
override fun build(
|
||||
url: NormalizedRelayUrl,
|
||||
out: WebSocketListener,
|
||||
): WebSocket = KtorWebSocket(url, httpClient, out)
|
||||
|
||||
companion object {
|
||||
fun defaultClient() =
|
||||
HttpClient(CIO) {
|
||||
install(WebSockets)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user