Sync using the new logic.

This commit is contained in:
Kgothatso Ngako committed 2026-04-19 04:30:29 +02:00
1 parent 56ba03c2b3
commit 403b245880
12 files changed
+193 -188

No files matched your search

+1
View File
@@ -45,6 +45,7 @@ kotlin {
commonMain.dependencies { commonMain.dependencies {
api("fr.acinq.lightning:lightning-kmp-core:1.11.5") api("fr.acinq.lightning:lightning-kmp-core:1.11.5")
implementation(libs.androidx.datastore)
implementation(libs.androidx.datastore.preferences) implementation(libs.androidx.datastore.preferences)
implementation(libs.androidx.lifecycle.viewmodelCompose) implementation(libs.androidx.lifecycle.viewmodelCompose)
@@ -20,15 +20,28 @@ import kotlinx.coroutines.flow.timeout
import kotlinx.coroutines.flow.transform import kotlinx.coroutines.flow.transform
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import ac.aux.compose.exceptions.NostrPublishException import ac.aux.compose.exceptions.NostrPublishException
import ac.aux.compose.network.dto.toRelayDTO
import ac.aux.compose.network.sockets.NostrIncomingMessage import ac.aux.compose.network.sockets.NostrIncomingMessage
import ac.aux.compose.network.sockets.NostrSocketClient import ac.aux.compose.network.sockets.NostrSocketClient
import ac.aux.compose.network.sockets.NostrSocketClientFactory import ac.aux.compose.network.sockets.NostrSocketClientFactory
import ac.aux.compose.network.sockets.SocketConnectionClosedCallback import ac.aux.compose.network.sockets.SocketConnectionClosedCallback
import ac.aux.compose.network.sockets.SocketConnectionOpenedCallback import ac.aux.compose.network.sockets.SocketConnectionOpenedCallback
import ac.aux.compose.network.sockets.filterByEventId import ac.aux.compose.network.sockets.filterByEventId
import ac.aux.compose.network.sockets.parseIncomingMessage import ac.aux.compose.network.sockets.filterBySubscriptionId
import ac.aux.compose.network.sockets.verifyOrThrow
import ac.aux.compose.repository.CachingImportRepository import ac.aux.compose.repository.CachingImportRepository
import co.touchlab.kermit.Logger import co.touchlab.kermit.Logger
import com.vitorpamplona.quartz.nip01Core.core.OptimizedJsonMapper
import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.ReqCmd
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.displayUrl
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.flow.transformWhile
import kotlin.time.Duration.Companion.seconds
/** /**
* As seen in Primal * As seen in Primal
@@ -114,10 +127,11 @@ class RelayPool(
val toAddRelayUrls = newRelayUrls.filter { it !in existingRelayUrls } val toAddRelayUrls = newRelayUrls.filter { it !in existingRelayUrls }
val toAddSocketClients = relays.filter { it.url in toAddRelayUrls }.mapAsNostrSocketClient() val toAddSocketClients = relays.filter { it.url in toAddRelayUrls }.mapAsNostrSocketClient()
logger.d("toAddSocketClients: ${toAddSocketClients.map { it.socketUrl }}" )
val newSocketClients = socketClients.toMutableList().apply { val newSocketClients = socketClients.toMutableList().apply {
addAll(toAddSocketClients) addAll(toAddSocketClients)
} }
logger.d("newSocketClients: ${newSocketClients.map { it.socketUrl }}")
socketClients = newSocketClients socketClients = newSocketClients
@@ -165,30 +179,60 @@ class RelayPool(
} }
} }
private suspend fun handleBroadcastEventThroughCachingProxy(relayUrls: List<String>, nostrEvent: NostrEvent) { @OptIn(FlowPreview::class)
val result = suspend fun query(reqCommand: ReqCmd, relayUrl: String): Pair<NostrIncomingMessage, List<NostrEvent>> {
cachingImportRepository.broadcastEvents( addRelays(
events = listOf(nostrEvent), setOf(
relays = relayUrls, NormalizedRelayUrl(relayUrl).url.toRelayDTO()
).getOrNull()
?: throw NostrPublishException(
cause = NetworkException(message = "Primal NostrEvent 10_000_149 not found or invalid."),
)
result.find { response -> response.eventId == nostrEvent.id }?.responses
?.mapNotNull { relayResponse ->
val relay = relayResponse.firstOrNull()
val responseMessage = relayResponse.getOrNull(index = 1)?.parseIncomingMessage()
if (relay != null && responseMessage != null) {
relay to responseMessage
} else {
null
}
}
?.find { (_, relayMessage) -> relayMessage is NostrIncomingMessage.OkMessage && relayMessage.success }
?: throw NostrPublishException(
cause = NetworkException("Event broadcast failed. Could not find success response from relays."),
) )
)
logger.d("socketClients: ${socketClients.map { it.socketUrl }}")
val nostrSocketClient = socketClients.find { NormalizedRelayUrl(it.socketUrl).displayUrl() == NormalizedRelayUrl(relayUrl).displayUrl() }
val filterRequest = OptimizedJsonMapper.toJson(reqCommand)
if (nostrSocketClient == null) {
throw NetworkException("$relayUrl is not connected")
}
return coroutineScope {
val deferredQueryResult = async { nostrSocketClient.collectQueryResult(reqCommand.subId) }
with(nostrSocketClient) {
sendMESSAGE(filterRequest)
}
deferredQueryResult.await()
}
}
private fun Flow<NostrIncomingMessage>.transformWhileEventsAreIncoming() =
transformWhile {
emit(it)
it is NostrIncomingMessage.EventMessage || it is NostrIncomingMessage.EventsMessage
}
@OptIn(FlowPreview::class)
private suspend fun NostrSocketClient.collectQueryResult(subscriptionId: String): Pair<NostrIncomingMessage, List<NostrEvent>> {
val messages = this.incomingMessages
.filterBySubscriptionId(id = subscriptionId)
.transformWhileEventsAreIncoming()
.timeout(15.seconds)
.toList()
val terminationMessage = messages.lastOrNull()
terminationMessage.verifyOrThrow(subscriptionId)
checkNotNull(terminationMessage)
val eventMessages = messages.filterIsInstance<NostrIncomingMessage.EventMessage>()
val eventsMessage = messages.filterIsInstance<NostrIncomingMessage.EventsMessage>()
val allNostrEvents = eventMessages.mapNotNull { it.nostrEvent } +
eventsMessage.map { it.nostrEvents }.flatten()
return Pair(
terminationMessage,
allNostrEvents,
)
} }
@OptIn(FlowPreview::class) @OptIn(FlowPreview::class)
@@ -16,6 +16,12 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import ac.aux.compose.exceptions.NostrPublishException import ac.aux.compose.exceptions.NostrPublishException
import ac.aux.compose.managers.CredentialsManager
import ac.aux.compose.managers.SeedManager
import ac.aux.compose.managers.bech32ToHexOrNull
import ac.aux.compose.managers.toHex
import ac.aux.compose.network.sockets.NostrIncomingMessage
import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.ReqCmd
/** /**
@@ -26,17 +32,12 @@ import ac.aux.compose.exceptions.NostrPublishException
class RelaysSocketManager constructor( class RelaysSocketManager constructor(
private val nostrSocketClientFactory: NostrSocketClientFactory, private val nostrSocketClientFactory: NostrSocketClientFactory,
private val cachingImportRepository: CachingImportRepository, private val cachingImportRepository: CachingImportRepository,
// private val activeAccountStore: ActiveAccountStore,
// private val usersDatabase: UsersDatabase,
private val relayRepository: RelayRepository private val relayRepository: RelayRepository
) { ) {
val logger = Logger.withTag("RelaysSocketManager") val logger = Logger.withTag("RelaysSocketManager")
private val scope = CoroutineScope(Dispatchers.IO) private val scope = CoroutineScope(Dispatchers.IO)
private val relayPoolsMutex = Mutex() private val relayPoolsMutex = Mutex()
private var relaysObserverJob: Job? = null
/** /**
* Concrete relay pool * Concrete relay pool
*/ */
@@ -56,27 +57,26 @@ class RelaysSocketManager constructor(
observeActiveUserId() observeActiveUserId()
} }
private val observeRelayJobs = mutableMapOf<String, Job>()
private fun observeActiveUserId() = private fun observeActiveUserId() =
scope.launch { scope.launch {
// TODO activeAccountStore.activeUserId.collect { userId -> SeedManager.activePublicKey().toHex()?.let { publicKey ->
// when { observeRelayJobs[publicKey]?.cancel()
// userId.isEmpty() -> {
// relaysObserverJob?.cancel() observeRelayJobs[publicKey] = observeRelays(publicKey)
// relaysObserverJob = null }
// clearRelayPools() // credentialsManager.credentials.collect { credentials ->
// } // credentials.forEach { credential ->
// credential.npub.bech32ToHexOrNull()?.let { publicKey ->
// observeRelayJobs[publicKey]?.cancel()
// //
// else -> { // observeRelayJobs[publicKey] = observeRelays(publicKey)
// relaysObserverJob?.cancel()
// relaysObserverJob = observeRelays(userId)
// } // }
// } // }
// } // }
} }
private suspend fun isCachingProxyEnabled() = false // TODO: activeAccountStore.activeUserAccount().cachingProxyEnabled
private fun observeRelays(publicKey: String): Job = private fun observeRelays(publicKey: String): Job =
scope.launch { scope.launch {
try { try {
@@ -129,7 +129,7 @@ class RelaysSocketManager constructor(
// customPool.closePool() // customPool.closePool()
} }
fun tryConnectingToAllUserRelays() { fun tryConnectingToAllRelays() {
relayPool.relays.forEach { relayPool.relays.forEach {
scope.launch { scope.launch {
relayPool.tryConnectingToRelay(it.url) relayPool.tryConnectingToRelay(it.url)
@@ -138,4 +138,11 @@ class RelaysSocketManager constructor(
} }
suspend fun tryConnectingToUserRelay(url: String) = relayPool.tryConnectingToRelay(url) suspend fun tryConnectingToUserRelay(url: String) = relayPool.tryConnectingToRelay(url)
suspend fun query(reqCommand: ReqCmd, relayUrl: String): Pair<NostrIncomingMessage, List<NostrEvent>> {
return relayPool.query(
reqCommand = reqCommand,
relayUrl = relayUrl
)
}
} }
@@ -1,6 +1,8 @@
package ac.aux.compose.network.sockets package ac.aux.compose.network.sockets
import ac.aux.compose.database.model.NostrEvent import ac.aux.compose.database.model.NostrEvent
import ac.aux.compose.exceptions.NetworkException
import ac.aux.compose.exceptions.NostrNoticeException
sealed class NostrIncomingMessage { sealed class NostrIncomingMessage {
@@ -38,3 +40,16 @@ sealed class NostrIncomingMessage {
val nostrEvents: List<NostrEvent> = emptyList(), val nostrEvents: List<NostrEvent> = emptyList(),
) : NostrIncomingMessage() ) : NostrIncomingMessage()
} }
fun NostrIncomingMessage?.verifyOrThrow(subscriptionId: String) {
if (this == null) {
throw NetworkException("No messages received.")
}
if (this is NostrIncomingMessage.NoticeMessage) {
throw NostrNoticeException(
reason = this.message,
subscriptionId = subscriptionId,
)
}
}
@@ -26,4 +26,6 @@ interface NostrSocketClient {
suspend fun sendEVENT(signedEvent: JsonObject) suspend fun sendEVENT(signedEvent: JsonObject)
suspend fun sendREQ(subscriptionId: String, data: JsonObject) suspend fun sendREQ(subscriptionId: String, data: JsonObject)
suspend fun sendMESSAGE(text: String, ensureSessionBeforeSend: Boolean = true)
} }
@@ -73,7 +73,7 @@ internal class NostrSocketClientImpl(
onSocketConnectionOpened?.invoke(url) onSocketConnectionOpened?.invoke(url)
if (incomingCompressionEnabled) { if (incomingCompressionEnabled) {
val id = Uuid.generateV4().toHexDashString() val id = Uuid.generateV4().toHexDashString()
sendMessage( sendMESSAGE(
text = """["REQ","$id",{"cache":["set_primal_protocol",{"compression":"zlib"}]}]""", text = """["REQ","$id",{"cache":["set_primal_protocol",{"compression":"zlib"}]}]""",
ensureSessionBeforeSend = false, ensureSessionBeforeSend = false,
) )
@@ -155,7 +155,7 @@ internal class NostrSocketClientImpl(
} }
} }
private suspend fun sendMessage(text: String, ensureSessionBeforeSend: Boolean = true) { override suspend fun sendMESSAGE(text: String, ensureSessionBeforeSend: Boolean) {
if (ensureSessionBeforeSend) { if (ensureSessionBeforeSend) {
ensureSocketConnectionOrThrow() ensureSocketConnectionOrThrow()
} }
@@ -165,21 +165,21 @@ internal class NostrSocketClientImpl(
override suspend fun sendREQ(subscriptionId: String, data: JsonObject) { override suspend fun sendREQ(subscriptionId: String, data: JsonObject) {
val reqMessage = data.buildNostrREQMessage(subscriptionId) val reqMessage = data.buildNostrREQMessage(subscriptionId)
return sendMessage(text = reqMessage) return sendMESSAGE(text = reqMessage)
} }
override suspend fun sendCOUNT(data: JsonObject): String { override suspend fun sendCOUNT(data: JsonObject): String {
val subscriptionId: String = Uuid.generateV4().toHexDashString() val subscriptionId: String = Uuid.generateV4().toHexDashString()
val reqMessage = data.buildNostrCOUNTMessage(subscriptionId) val reqMessage = data.buildNostrCOUNTMessage(subscriptionId)
sendMessage(text = reqMessage) sendMESSAGE(text = reqMessage)
return subscriptionId return subscriptionId
} }
override suspend fun sendCLOSE(subscriptionId: String) = sendMessage(text = subscriptionId.buildNostrCLOSEMessage()) override suspend fun sendCLOSE(subscriptionId: String) = sendMESSAGE(text = subscriptionId.buildNostrCLOSEMessage())
override suspend fun sendEVENT(signedEvent: JsonObject) = sendMessage(text = signedEvent.buildNostrEVENTMessage()) override suspend fun sendEVENT(signedEvent: JsonObject) = sendMESSAGE(text = signedEvent.buildNostrEVENTMessage())
override suspend fun sendAUTH(signedEvent: JsonObject) = sendMessage(text = signedEvent.buildNostrAUTHMessage()) override suspend fun sendAUTH(signedEvent: JsonObject) = sendMESSAGE(text = signedEvent.buildNostrAUTHMessage())
private fun logLargeText( private fun logLargeText(
text: String, text: String,
@@ -35,7 +35,7 @@ object Relays {
val bootstrapInboxRelaySet = setOf(damus, primal, mom, nos, bitcoiner, oxtr, yabu) val bootstrapInboxRelaySet = setOf(damus, primal, mom, nos, bitcoiner, oxtr, yabu)
val eventFinderRelaySet = setOf(wine, damus, primal, mom, nos, bitcoiner, oxtr) val eventFinderRelaySet = setOf(wine, damus, primal, mom, nos, bitcoiner, oxtr)
val eventPublishRelaySet = setOf(damus, primal, mom, nos, bitcoiner, oxtr) val eventPublishRelaySet = setOf(primal, mom, nos, bitcoiner, oxtr)
val DefaultNIP65RelaySet = setOf(mom, nos, bitcoiner) val DefaultNIP65RelaySet = setOf(mom, nos, bitcoiner)
@@ -7,11 +7,13 @@ import ac.aux.compose.exceptions.SignatureException
import ac.aux.compose.exceptions.SigningKeyNotFoundException import ac.aux.compose.exceptions.SigningKeyNotFoundException
import ac.aux.compose.exceptions.SigningRejectedException import ac.aux.compose.exceptions.SigningRejectedException
import ac.aux.compose.managers.CredentialsManager import ac.aux.compose.managers.CredentialsManager
import ac.aux.compose.managers.SeedManager
import ac.aux.compose.managers.hexToNpubHrp import ac.aux.compose.managers.hexToNpubHrp
import ac.aux.compose.network.UserAgent import ac.aux.compose.network.UserAgent
import ac.aux.compose.network.asClientTag import ac.aux.compose.network.asClientTag
import ac.aux.compose.network.dto.RelayDTO import ac.aux.compose.network.dto.RelayDTO
import com.vitorpamplona.quartz.nip19Bech32.toNpub import com.vitorpamplona.quartz.nip19Bech32.toNpub
import com.vitorpamplona.quartz.nip19Bech32.toNsec
import com.vitorpamplona.quartz.nip51Lists.relayLists.RelayFeedsListEvent import com.vitorpamplona.quartz.nip51Lists.relayLists.RelayFeedsListEvent
import fr.acinq.secp256k1.Hex import fr.acinq.secp256k1.Hex
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@@ -31,7 +33,6 @@ import kotlinx.coroutines.sync.withLock
*/ */
class NostrNotaryRepository( class NostrNotaryRepository(
private val nostrRepository: NostrRepository, private val nostrRepository: NostrRepository,
private val credentialsStore: CredentialsManager,
) { ) {
private val scope = CoroutineScope(Dispatchers.Main) private val scope = CoroutineScope(Dispatchers.Main)
@@ -75,13 +76,15 @@ class NostrNotaryRepository(
private fun findNsecOrThrow(pubkey: String): String = private fun findNsecOrThrow(pubkey: String): String =
runCatching { runCatching {
val npub = Hex.decode(pubkey).toNpub() // val npub = Hex.decode(pubkey).toNpub()
credentialsStore.findOrThrow(npub = npub).nsec // credentialsStore.findOrThrow(npub = npub).nsec
SeedManager.activeKeyPair().privKey?.toNsec()
}.getOrNull() ?: throw SigningKeyNotFoundException() }.getOrNull() ?: throw SigningKeyNotFoundException()
private fun signNostrEvent(publicKey: String, event: UnsignedNostrEvent): NostrEvent? { private fun signNostrEvent(publicKey: String, event: UnsignedNostrEvent): NostrEvent? {
val isExternalSignerLogin = runCatching { val isExternalSignerLogin = runCatching {
credentialsStore.isExternalSignerCredential(npub = publicKey.hexToNpubHrp()) // credentialsStore.isExternalSignerCredential(npub = publicKey.hexToNpubHrp())
false
}.getOrDefault(false) }.getOrDefault(false)
if (isExternalSignerLogin) { if (isExternalSignerLogin) {
@@ -59,11 +59,9 @@ class NavigationViewModel(
val relaysSocketManager = RelaysSocketManager( val relaysSocketManager = RelaysSocketManager(
nostrSocketClientFactory = NostrSocketClientFactory, nostrSocketClientFactory = NostrSocketClientFactory,
cachingImportRepository = CachingImportRepository.NO_OP_CACHING_IMPORT_REPOSITORY, cachingImportRepository = CachingImportRepository.NO_OP_CACHING_IMPORT_REPOSITORY,
relayRepository = relayRepository relayRepository = relayRepository,
) )
companion object { companion object {
private const val TAG = "NavigationViewModel" private const val TAG = "NavigationViewModel"
@@ -86,8 +84,6 @@ class NavigationViewModel(
private val logger = Logger.withTag(TAG) private val logger = Logger.withTag(TAG)
private val syncingJobs = mutableMapOf<String, Job>()
val httpClient = HttpClient() { val httpClient = HttpClient() {
install(WebSockets) { install(WebSockets) {
contentConverter = KotlinxWebsocketSerializationConverter(Json { contentConverter = KotlinxWebsocketSerializationConverter(Json {
@@ -98,10 +94,6 @@ class NavigationViewModel(
} }
} }
val nostrEventBroadcaster = NostrEventBroadcaster(
scope = scope
)
private val _navigationUIState = MutableStateFlow( private val _navigationUIState = MutableStateFlow(
initialNavigationUIState initialNavigationUIState
) )
@@ -156,8 +148,6 @@ class NavigationViewModel(
private fun observePendingSyncNostrEventRequests() { private fun observePendingSyncNostrEventRequests() {
logger.i { "observePendingSyncNostrEventRequests" } logger.i { "observePendingSyncNostrEventRequests" }
val syncEventCache = mutableMapOf<String, NostrEvent>()
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
nostrRepository.observePendingSynchronizeNostrEventRequests().collect { synchronizeNostrEventRequestOrNull -> nostrRepository.observePendingSynchronizeNostrEventRequests().collect { synchronizeNostrEventRequestOrNull ->
synchronizeNostrEventRequestOrNull?.let { synchronizeNostrEventRequest -> synchronizeNostrEventRequestOrNull?.let { synchronizeNostrEventRequest ->
@@ -179,120 +169,32 @@ class NavigationViewModel(
} }
) )
scope.launch { scope.launch {
try { try {
val webSocketSession = httpClient.webSocketSession(
urlString = synchronizeNostrEventRequest.relayURL val result = relaysSocketManager.query(
reqCommand,
synchronizeNostrEventRequest.relayURL
) )
// TODO: don't make a new sync job if there's an active job? logger.d("Result: $result")
syncingJobs[synchronizeNostrEventRequest.relayURL] = scope.launch {
val filterRequest = OptimizedJsonMapper.toJson(reqCommand) result.second.forEach { nostrEvent ->
logger.d("Syncing: $filterRequest") nostrRepository.saveNostrEvent(
webSocketSession.send( nostrEvent = nostrEvent,
frame = Frame.Text(filterRequest) synchronizeNostrEventRequest,
synchronizationRelayURLs = Relays.eventPublishRelaySet.map { normalizedRelayUrl -> normalizedRelayUrl.url } + synchronizeNostrEventRequest.relayURL
) )
nostrRepository.synchronizeNostrEventRequestProcessed(synchronizeNostrEventRequest)
webSocketSession.incoming.consumeAsFlow().collect { frame ->
when(frame) {
is Frame.Text -> {
frame.readText().let { text ->
try {
logger.d("WebSocket Read Text: $text")
val result = Json.decodeFromString<JsonArray>(text)
if (result.firstOrNull()?.text == "EVENT") {
result.getOrNull(1)?.text?.let { subscriptionId ->
if (subscriptionId == synchronizeNostrEventRequest.id) {
result.getOrNull(2)?.jsonObject?.toString()?.let { jsonText ->
logger.i("Processing: $jsonText")
val event = Event.fromJson(
jsonText
)
// TODO: Do profile specific cache (to avoid wiping an updated profile)
syncEventCache.getOrPut(event.id, {
val taggedEvent = event.firstTaggedEvent()
val taggedUser = event.firstTaggedUser()
val nostrEvent = NostrEvent(
id = event.id,
pubKey = event.pubKey,
kind = event.kind,
content = event.content,
tags = event.tags,
createdAt = Instant.fromEpochSeconds(event.createdAt),
unsignedNostrEventId = synchronizeNostrEventRequest.unsignedNostrEventId,
sig = event.sig,
taggedNostrEventId = taggedEvent?.eventId,
taggedNostrEventRelayUrl = taggedEvent?.relay?.url,
taggedPublicKey = taggedUser?.pubKey,
taggedPublicKeyRelayUrl = taggedUser?.relayHint?.url
)
nostrRepository.saveNostrEvent(
nostrEvent = nostrEvent,
synchronizeNostrEventRequest,
synchronizationRelayURLs = Relays.eventPublishRelaySet.map { normalizedRelayUrl -> normalizedRelayUrl.url }
)
nostrEvent
})
}
}
}
} else if(result.firstOrNull()?.text == "EOSE") {
result.getOrNull(1)?.text?.let { subscriptionId ->
if (subscriptionId == synchronizeNostrEventRequest.id) {
// Close the job
logger.d("We need to close the job")
}
}
} else {
logger.d("Unsupported: $text")
}
} catch (e: Throwable) {
logger.e("Error processing receipt: $text", e)
}
}
// Syncing doesn't necessary have to close after result..
webSocketSession.close()
syncingJobs.remove(synchronizeNostrEventRequest.relayURL)?.cancel()
}
is Frame.Close -> {
logger.d("Close $frame")
}
is Frame.Ping -> {
logger.d("Ping: $frame")
}
is Frame.Pong -> {
logger.d("Pong: $frame")
}
is Frame.Binary -> {
logger.d("Binary Message: $frame")
}
else -> {
logger.d("Unsupported frame: $frame")
}
}
}
logger.i("Websocket exit")
} }
syncingJobs[synchronizeNostrEventRequest.relayURL]?.join()
} catch (e: Throwable) {
logger.e("Relay Error (${synchronizeNostrEventRequest.relayURL})", e)
// TODO: Have it failed instead of processed...
nostrRepository.synchronizeNostrEventRequestProcessed(synchronizeNostrEventRequest) nostrRepository.synchronizeNostrEventRequestProcessed(synchronizeNostrEventRequest)
} catch (e: Throwable) {
logger.e("Failed to sync", e)
} }
} }
} }
} }
} }
@@ -325,24 +227,6 @@ class NavigationViewModel(
) )
) )
) )
// nostrEventBroadcaster.broadcastEvent(
// localBroadcastNostrEventRequest = localBroadcastNostrEventRequest,
// onBroadcastRequestProcessed = { broadcastNostrEventRequest ->
// scope.launch(Dispatchers.IO) {
// nostrRepository.broadcastProcessed(
// broadcastNostrEventRequest
// )
// }
// },
// onBroadcastReceipt = { broadcastNostrEventReceipt ->
// scope.launch(Dispatchers.IO) {
// nostrRepository.saveBroadcastReceipt(
// broadcastNostrEventReceipt
// )
// }
// }
// )
} }
} }
} }
@@ -0,0 +1,33 @@
package ac.aux.compose.managers
import ac.aux.compose.PlatformContext
import androidx.datastore.core.Storage
import androidx.datastore.core.okio.OkioSerializer
import androidx.datastore.core.okio.OkioStorage
import okio.FileSystem
import okio.Path.Companion.toPath
import platform.Foundation.NSDocumentDirectory
import platform.Foundation.NSFileManager
import platform.Foundation.NSURL
import platform.Foundation.NSUserDomainMask
actual fun computeDatastoreStorage(
platformContext: PlatformContext,
dataStoreFileName: String
): Storage<Set<CredentialsManager.Credential>> {
return OkioStorage(
fileSystem = FileSystem.SYSTEM,
serializer = OkioSerializer<Set<CredentialsManager.Credential>>,
producePath = {
val documentDirectory: NSURL? = NSFileManager.defaultManager.URLsForDirectory(
directory = NSDocumentDirectory,
inDomains = NSUserDomainMask,
appropriateForURL = null,
create = false,
error = null
)
(requireNotNull(documentDirectory).path + "/$dataStoreFileName").toPath()
}
)
}
@@ -0,0 +1,15 @@
package ac.aux.compose.managers
import ac.aux.compose.PlatformContext
import androidx.datastore.core.Storage
import io.ktor.client.plugins.cache.storage.FileStorage
actual fun computeDatastoreStorage(
platformContext: PlatformContext,
dataStoreFileName: String
): Storage<Set<CredentialsManager.Credential>> {
FileStorage(
directory = ""
)
TODO("Not yet implemented")
}
+1
View File
@@ -40,6 +40,7 @@ androidx-testExt-junit = { module = "androidx.test.ext:junit", version.ref = "an
androidx-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "androidx-espresso" } androidx-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "androidx-espresso" }
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" } androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" }
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity" } androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity" }
androidx-datastore = { module = "androidx.datastore:datastore", version.ref = "datastorePreferences" }
androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" } androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" }
androidx-paging-common = { module = "androidx.paging:paging-common", version.ref = "pagingCommon" } androidx-paging-common = { module = "androidx.paging:paging-common", version.ref = "pagingCommon" }
androidx-paging-compose = { module = "androidx.paging:paging-compose", version.ref = "pagingCommon" } androidx-paging-compose = { module = "androidx.paging:paging-compose", version.ref = "pagingCommon" }