Sync using the new logic.
This commit is contained in:
@@ -45,6 +45,7 @@ kotlin {
|
||||
commonMain.dependencies {
|
||||
api("fr.acinq.lightning:lightning-kmp-core:1.11.5")
|
||||
|
||||
implementation(libs.androidx.datastore)
|
||||
implementation(libs.androidx.datastore.preferences)
|
||||
|
||||
implementation(libs.androidx.lifecycle.viewmodelCompose)
|
||||
|
||||
@@ -20,15 +20,28 @@ import kotlinx.coroutines.flow.timeout
|
||||
import kotlinx.coroutines.flow.transform
|
||||
import kotlinx.coroutines.launch
|
||||
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.NostrSocketClient
|
||||
import ac.aux.compose.network.sockets.NostrSocketClientFactory
|
||||
import ac.aux.compose.network.sockets.SocketConnectionClosedCallback
|
||||
import ac.aux.compose.network.sockets.SocketConnectionOpenedCallback
|
||||
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 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
|
||||
@@ -114,10 +127,11 @@ class RelayPool(
|
||||
|
||||
val toAddRelayUrls = newRelayUrls.filter { it !in existingRelayUrls }
|
||||
val toAddSocketClients = relays.filter { it.url in toAddRelayUrls }.mapAsNostrSocketClient()
|
||||
|
||||
logger.d("toAddSocketClients: ${toAddSocketClients.map { it.socketUrl }}" )
|
||||
val newSocketClients = socketClients.toMutableList().apply {
|
||||
addAll(toAddSocketClients)
|
||||
}
|
||||
logger.d("newSocketClients: ${newSocketClients.map { it.socketUrl }}")
|
||||
|
||||
socketClients = newSocketClients
|
||||
|
||||
@@ -165,30 +179,60 @@ class RelayPool(
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun handleBroadcastEventThroughCachingProxy(relayUrls: List<String>, nostrEvent: NostrEvent) {
|
||||
val result =
|
||||
cachingImportRepository.broadcastEvents(
|
||||
events = listOf(nostrEvent),
|
||||
relays = relayUrls,
|
||||
).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."),
|
||||
@OptIn(FlowPreview::class)
|
||||
suspend fun query(reqCommand: ReqCmd, relayUrl: String): Pair<NostrIncomingMessage, List<NostrEvent>> {
|
||||
addRelays(
|
||||
setOf(
|
||||
NormalizedRelayUrl(relayUrl).url.toRelayDTO()
|
||||
)
|
||||
)
|
||||
|
||||
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)
|
||||
|
||||
@@ -16,6 +16,12 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
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(
|
||||
private val nostrSocketClientFactory: NostrSocketClientFactory,
|
||||
private val cachingImportRepository: CachingImportRepository,
|
||||
// private val activeAccountStore: ActiveAccountStore,
|
||||
// private val usersDatabase: UsersDatabase,
|
||||
private val relayRepository: RelayRepository
|
||||
) {
|
||||
val logger = Logger.withTag("RelaysSocketManager")
|
||||
private val scope = CoroutineScope(Dispatchers.IO)
|
||||
private val relayPoolsMutex = Mutex()
|
||||
|
||||
private var relaysObserverJob: Job? = null
|
||||
|
||||
|
||||
/**
|
||||
* Concrete relay pool
|
||||
*/
|
||||
@@ -56,27 +57,26 @@ class RelaysSocketManager constructor(
|
||||
observeActiveUserId()
|
||||
}
|
||||
|
||||
private val observeRelayJobs = mutableMapOf<String, Job>()
|
||||
|
||||
private fun observeActiveUserId() =
|
||||
scope.launch {
|
||||
// TODO activeAccountStore.activeUserId.collect { userId ->
|
||||
// when {
|
||||
// userId.isEmpty() -> {
|
||||
// relaysObserverJob?.cancel()
|
||||
// relaysObserverJob = null
|
||||
// clearRelayPools()
|
||||
// }
|
||||
SeedManager.activePublicKey().toHex()?.let { publicKey ->
|
||||
observeRelayJobs[publicKey]?.cancel()
|
||||
|
||||
observeRelayJobs[publicKey] = observeRelays(publicKey)
|
||||
}
|
||||
// credentialsManager.credentials.collect { credentials ->
|
||||
// credentials.forEach { credential ->
|
||||
// credential.npub.bech32ToHexOrNull()?.let { publicKey ->
|
||||
// observeRelayJobs[publicKey]?.cancel()
|
||||
//
|
||||
// else -> {
|
||||
// relaysObserverJob?.cancel()
|
||||
// relaysObserverJob = observeRelays(userId)
|
||||
// observeRelayJobs[publicKey] = observeRelays(publicKey)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
private suspend fun isCachingProxyEnabled() = false // TODO: activeAccountStore.activeUserAccount().cachingProxyEnabled
|
||||
|
||||
private fun observeRelays(publicKey: String): Job =
|
||||
scope.launch {
|
||||
try {
|
||||
@@ -129,7 +129,7 @@ class RelaysSocketManager constructor(
|
||||
// customPool.closePool()
|
||||
}
|
||||
|
||||
fun tryConnectingToAllUserRelays() {
|
||||
fun tryConnectingToAllRelays() {
|
||||
relayPool.relays.forEach {
|
||||
scope.launch {
|
||||
relayPool.tryConnectingToRelay(it.url)
|
||||
@@ -138,4 +138,11 @@ class RelaysSocketManager constructor(
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
import ac.aux.compose.database.model.NostrEvent
|
||||
import ac.aux.compose.exceptions.NetworkException
|
||||
import ac.aux.compose.exceptions.NostrNoticeException
|
||||
|
||||
sealed class NostrIncomingMessage {
|
||||
|
||||
@@ -38,3 +40,16 @@ sealed class NostrIncomingMessage {
|
||||
val nostrEvents: List<NostrEvent> = emptyList(),
|
||||
) : 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 sendREQ(subscriptionId: String, data: JsonObject)
|
||||
|
||||
suspend fun sendMESSAGE(text: String, ensureSessionBeforeSend: Boolean = true)
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ internal class NostrSocketClientImpl(
|
||||
onSocketConnectionOpened?.invoke(url)
|
||||
if (incomingCompressionEnabled) {
|
||||
val id = Uuid.generateV4().toHexDashString()
|
||||
sendMessage(
|
||||
sendMESSAGE(
|
||||
text = """["REQ","$id",{"cache":["set_primal_protocol",{"compression":"zlib"}]}]""",
|
||||
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) {
|
||||
ensureSocketConnectionOrThrow()
|
||||
}
|
||||
@@ -165,21 +165,21 @@ internal class NostrSocketClientImpl(
|
||||
|
||||
override suspend fun sendREQ(subscriptionId: String, data: JsonObject) {
|
||||
val reqMessage = data.buildNostrREQMessage(subscriptionId)
|
||||
return sendMessage(text = reqMessage)
|
||||
return sendMESSAGE(text = reqMessage)
|
||||
}
|
||||
|
||||
override suspend fun sendCOUNT(data: JsonObject): String {
|
||||
val subscriptionId: String = Uuid.generateV4().toHexDashString()
|
||||
val reqMessage = data.buildNostrCOUNTMessage(subscriptionId)
|
||||
sendMessage(text = reqMessage)
|
||||
sendMESSAGE(text = reqMessage)
|
||||
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(
|
||||
text: String,
|
||||
|
||||
@@ -35,7 +35,7 @@ object Relays {
|
||||
|
||||
val bootstrapInboxRelaySet = setOf(damus, primal, mom, nos, bitcoiner, oxtr, yabu)
|
||||
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)
|
||||
|
||||
@@ -7,11 +7,13 @@ import ac.aux.compose.exceptions.SignatureException
|
||||
import ac.aux.compose.exceptions.SigningKeyNotFoundException
|
||||
import ac.aux.compose.exceptions.SigningRejectedException
|
||||
import ac.aux.compose.managers.CredentialsManager
|
||||
import ac.aux.compose.managers.SeedManager
|
||||
import ac.aux.compose.managers.hexToNpubHrp
|
||||
import ac.aux.compose.network.UserAgent
|
||||
import ac.aux.compose.network.asClientTag
|
||||
import ac.aux.compose.network.dto.RelayDTO
|
||||
import com.vitorpamplona.quartz.nip19Bech32.toNpub
|
||||
import com.vitorpamplona.quartz.nip19Bech32.toNsec
|
||||
import com.vitorpamplona.quartz.nip51Lists.relayLists.RelayFeedsListEvent
|
||||
import fr.acinq.secp256k1.Hex
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -31,7 +33,6 @@ import kotlinx.coroutines.sync.withLock
|
||||
*/
|
||||
class NostrNotaryRepository(
|
||||
private val nostrRepository: NostrRepository,
|
||||
private val credentialsStore: CredentialsManager,
|
||||
) {
|
||||
private val scope = CoroutineScope(Dispatchers.Main)
|
||||
|
||||
@@ -75,13 +76,15 @@ class NostrNotaryRepository(
|
||||
|
||||
private fun findNsecOrThrow(pubkey: String): String =
|
||||
runCatching {
|
||||
val npub = Hex.decode(pubkey).toNpub()
|
||||
credentialsStore.findOrThrow(npub = npub).nsec
|
||||
// val npub = Hex.decode(pubkey).toNpub()
|
||||
// credentialsStore.findOrThrow(npub = npub).nsec
|
||||
SeedManager.activeKeyPair().privKey?.toNsec()
|
||||
}.getOrNull() ?: throw SigningKeyNotFoundException()
|
||||
|
||||
private fun signNostrEvent(publicKey: String, event: UnsignedNostrEvent): NostrEvent? {
|
||||
val isExternalSignerLogin = runCatching {
|
||||
credentialsStore.isExternalSignerCredential(npub = publicKey.hexToNpubHrp())
|
||||
// credentialsStore.isExternalSignerCredential(npub = publicKey.hexToNpubHrp())
|
||||
false
|
||||
}.getOrDefault(false)
|
||||
|
||||
if (isExternalSignerLogin) {
|
||||
|
||||
@@ -59,11 +59,9 @@ class NavigationViewModel(
|
||||
val relaysSocketManager = RelaysSocketManager(
|
||||
nostrSocketClientFactory = NostrSocketClientFactory,
|
||||
cachingImportRepository = CachingImportRepository.NO_OP_CACHING_IMPORT_REPOSITORY,
|
||||
relayRepository = relayRepository
|
||||
relayRepository = relayRepository,
|
||||
)
|
||||
|
||||
|
||||
|
||||
companion object {
|
||||
private const val TAG = "NavigationViewModel"
|
||||
|
||||
@@ -86,8 +84,6 @@ class NavigationViewModel(
|
||||
|
||||
private val logger = Logger.withTag(TAG)
|
||||
|
||||
private val syncingJobs = mutableMapOf<String, Job>()
|
||||
|
||||
val httpClient = HttpClient() {
|
||||
install(WebSockets) {
|
||||
contentConverter = KotlinxWebsocketSerializationConverter(Json {
|
||||
@@ -98,10 +94,6 @@ class NavigationViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
val nostrEventBroadcaster = NostrEventBroadcaster(
|
||||
scope = scope
|
||||
)
|
||||
|
||||
private val _navigationUIState = MutableStateFlow(
|
||||
initialNavigationUIState
|
||||
)
|
||||
@@ -156,8 +148,6 @@ class NavigationViewModel(
|
||||
private fun observePendingSyncNostrEventRequests() {
|
||||
logger.i { "observePendingSyncNostrEventRequests" }
|
||||
|
||||
val syncEventCache = mutableMapOf<String, NostrEvent>()
|
||||
|
||||
scope.launch(Dispatchers.IO) {
|
||||
nostrRepository.observePendingSynchronizeNostrEventRequests().collect { synchronizeNostrEventRequestOrNull ->
|
||||
synchronizeNostrEventRequestOrNull?.let { synchronizeNostrEventRequest ->
|
||||
@@ -179,120 +169,32 @@ class NavigationViewModel(
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
scope.launch {
|
||||
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?
|
||||
syncingJobs[synchronizeNostrEventRequest.relayURL] = scope.launch {
|
||||
val filterRequest = OptimizedJsonMapper.toJson(reqCommand)
|
||||
logger.d("Syncing: $filterRequest")
|
||||
webSocketSession.send(
|
||||
frame = Frame.Text(filterRequest)
|
||||
logger.d("Result: $result")
|
||||
|
||||
result.second.forEach { nostrEvent ->
|
||||
nostrRepository.saveNostrEvent(
|
||||
nostrEvent = nostrEvent,
|
||||
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)
|
||||
} 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")
|
||||
}
|
||||
@@ -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-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" }
|
||||
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-paging-common = { module = "androidx.paging:paging-common", version.ref = "pagingCommon" }
|
||||
androidx-paging-compose = { module = "androidx.paging:paging-compose", version.ref = "pagingCommon" }
|
||||
|
||||
Reference in New Issue
Block a user