Bug fix on infinite sync
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 1,
|
||||
"identityHash": "75e5c80b2c3183c8a4fa300627ffb5db",
|
||||
"identityHash": "9a3031f0a4cb56c9b5d6c26be0f48a3c",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "BroadcastNostrEventReceipt",
|
||||
@@ -873,7 +873,7 @@
|
||||
},
|
||||
{
|
||||
"tableName": "SynchronizeNostrEventRequest",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `status` TEXT NOT NULL, `relayURL` TEXT NOT NULL, `eventIds` TEXT, `authorPublicKeys` TEXT, `kinds` TEXT, `tagName` TEXT, `since` INTEGER, `until` INTEGER, `limit` INTEGER, `nostrEventId` TEXT, `unsignedNostrEventId` INTEGER, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`nostrEventId`) REFERENCES `NostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`unsignedNostrEventId`) REFERENCES `UnsignedNostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `status` TEXT NOT NULL, `relayURL` TEXT NOT NULL, `eventIds` TEXT, `authorPublicKeys` TEXT, `kinds` TEXT, `tagName` TEXT, `since` INTEGER, `until` INTEGER, `limit` INTEGER, `search` TEXT, `nostrEventId` TEXT, `unsignedNostrEventId` INTEGER, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`nostrEventId`) REFERENCES `NostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`unsignedNostrEventId`) REFERENCES `UnsignedNostrEvent`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
@@ -928,6 +928,11 @@
|
||||
"columnName": "limit",
|
||||
"affinity": "INTEGER"
|
||||
},
|
||||
{
|
||||
"fieldPath": "search",
|
||||
"columnName": "search",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "nostrEventId",
|
||||
"columnName": "nostrEventId",
|
||||
@@ -1330,7 +1335,7 @@
|
||||
],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '75e5c80b2c3183c8a4fa300627ffb5db')"
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '9a3031f0a4cb56c9b5d6c26be0f48a3c')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package ac.aux.compose.database
|
||||
|
||||
import ac.aux.compose.database.converters.Converters
|
||||
import ac.aux.compose.database.converters.AuxConverters
|
||||
import ac.aux.compose.database.dao.BroadcastNostrEventReceiptDao
|
||||
import ac.aux.compose.database.dao.BroadcastNostrEventRequestDao
|
||||
import ac.aux.compose.database.dao.NostrDao
|
||||
@@ -46,7 +46,7 @@ import androidx.room.useWriterConnection
|
||||
],
|
||||
version = 1
|
||||
)
|
||||
@TypeConverters(Converters::class)
|
||||
@TypeConverters(AuxConverters::class)
|
||||
abstract class AuxDatabase: RoomDatabase() {
|
||||
abstract fun broadcastNostrEventReceiptDao(): BroadcastNostrEventReceiptDao
|
||||
abstract fun broadcastNostrEventRequestDao(): BroadcastNostrEventRequestDao
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
package ac.aux.compose.database.converters
|
||||
|
||||
import ac.aux.compose.database.model.typealiases.HexArray
|
||||
import ac.aux.compose.database.model.typealiases.KindArray
|
||||
import androidx.room.TypeConverter
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArray
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlin.time.Instant
|
||||
|
||||
class Converters {
|
||||
class AuxConverters {
|
||||
val logger = Logger.withTag("AuxConverters")
|
||||
|
||||
@TypeConverter
|
||||
fun fromTimestamp(value: Long?): Instant? {
|
||||
return value?.let { Instant.fromEpochMilliseconds(it) }
|
||||
@@ -27,4 +32,45 @@ class Converters {
|
||||
// Decodes the JSON string back into the nested array structure
|
||||
return value?.let { Json.decodeFromString<TagArray>(it) }
|
||||
}
|
||||
|
||||
|
||||
@TypeConverter
|
||||
fun fromHexArray(value: HexArray?): String? = try {
|
||||
return value?.let {
|
||||
Json.encodeToString(it).lowercase()
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
logger.e("Failed to convert from HexArray $value", e)
|
||||
return null
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toHexArray(value: String?): HexArray? = try {
|
||||
return value?.let {
|
||||
Json.decodeFromString<HexArray>(it)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
logger.e("Failed to convert to HexArray $value", e)
|
||||
return null
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun fromKindArray(value: KindArray?): String? = try {
|
||||
return value?.let {
|
||||
Json.encodeToString(it)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
logger.e("Failed to convert from KindArray $value", e)
|
||||
return null
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toKindArray(value: String?): KindArray? = try {
|
||||
return value?.let {
|
||||
Json.decodeFromString<KindArray>(it)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
logger.e("Failed to convert to KindArray $value", e)
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -83,36 +83,38 @@ abstract class NostrDao(
|
||||
synchronizationRelayURLs: List<String>
|
||||
) {
|
||||
logger.i("Store Nostr Event: $nostrEvent")
|
||||
// Find or create profile with the pubKey... if not found submit a sync request...
|
||||
val profile = database.profileDao().getProfileByPublicKey(nostrEvent.pubKey)
|
||||
if (nostrEvent.unsignedNostrEventId == null) {
|
||||
// Find or create profile with the pubKey... if not found submit a sync request...
|
||||
val profile = database.profileDao().getProfileByPublicKey(nostrEvent.pubKey)
|
||||
|
||||
if (profile == null) {
|
||||
val placeHolderProfile = Profile(
|
||||
publicKey = nostrEvent.pubKey,
|
||||
nostrEventId = nostrEvent.pubKey // This is illegal... because the nostrEventId should be the one with all the profile information
|
||||
)
|
||||
val placeHolderProfileNostrEvent = NostrEvent(
|
||||
id = placeHolderProfile.publicKey,
|
||||
kind = 30024,
|
||||
pubKey = placeHolderProfile.publicKey,
|
||||
content = placeHolderProfile.publicKey,
|
||||
createdAt = Clock.System.now(),
|
||||
sig = placeHolderProfile.publicKey,
|
||||
tags = emptyArray(),
|
||||
)
|
||||
database.nostrEventDao().upsert(placeHolderProfileNostrEvent)
|
||||
database.profileDao().upsert(placeHolderProfile)
|
||||
|
||||
synchronizationRelayURLs.forEach { synchronizationRelayURL ->
|
||||
database.synchronizeNostrEventRequestDao().upsert(
|
||||
SynchronizeNostrEventRequest(
|
||||
authorPublicKeys = listOf<String>(nostrEvent.pubKey).joinToString(","),
|
||||
relayURL = synchronizationRelayURL
|
||||
)
|
||||
if (profile == null) {
|
||||
val placeHolderProfile = Profile(
|
||||
publicKey = nostrEvent.pubKey,
|
||||
nostrEventId = nostrEvent.pubKey // This is illegal... because the nostrEventId should be the one with all the profile information
|
||||
)
|
||||
val placeHolderProfileNostrEvent = NostrEvent(
|
||||
id = placeHolderProfile.publicKey,
|
||||
kind = 30024,
|
||||
pubKey = placeHolderProfile.publicKey,
|
||||
content = placeHolderProfile.publicKey,
|
||||
createdAt = Clock.System.now(),
|
||||
sig = placeHolderProfile.publicKey,
|
||||
tags = emptyArray(),
|
||||
)
|
||||
database.nostrEventDao().upsert(placeHolderProfileNostrEvent)
|
||||
database.profileDao().upsert(placeHolderProfile)
|
||||
|
||||
synchronizationRelayURLs.forEach { synchronizationRelayURL ->
|
||||
database.synchronizeNostrEventRequestDao().upsert(
|
||||
SynchronizeNostrEventRequest(
|
||||
authorPublicKeys = arrayOf(nostrEvent.pubKey),
|
||||
kinds = arrayOf(0),
|
||||
relayURL = synchronizationRelayURL
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
database.nostrEventDao().upsert(nostrEvent)
|
||||
|
||||
// Index nostrEvent
|
||||
@@ -146,7 +148,7 @@ abstract class NostrDao(
|
||||
synchronizationRelayURLs.forEach { synchronizationRelayURL ->
|
||||
database.synchronizeNostrEventRequestDao().upsert(
|
||||
SynchronizeNostrEventRequest(
|
||||
eventIds = listOf(post.replyToId).joinToString(","),
|
||||
eventIds = arrayOf(post.replyToId),
|
||||
relayURL = synchronizationRelayURL
|
||||
)
|
||||
)
|
||||
@@ -171,7 +173,7 @@ abstract class NostrDao(
|
||||
synchronizationRelayURLs.forEach { synchronizationRelayURL ->
|
||||
database.synchronizeNostrEventRequestDao().upsert(
|
||||
SynchronizeNostrEventRequest(
|
||||
eventIds = listOf(post.repostId).joinToString(","),
|
||||
eventIds = arrayOf(post.repostId),
|
||||
relayURL = synchronizationRelayURL
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package ac.aux.compose.database.model
|
||||
|
||||
import ac.aux.compose.database.model.traits.NostrEventEntity
|
||||
import ac.aux.compose.database.model.traits.OptionalNostrEventEntity
|
||||
import ac.aux.compose.database.model.traits.TimestampedEntity
|
||||
import ac.aux.compose.database.model.traits.UnsignedNostrEventEntity
|
||||
import ac.aux.compose.database.model.typealiases.HexArray
|
||||
import ac.aux.compose.database.model.typealiases.KindArray
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.Index
|
||||
@@ -40,16 +41,59 @@ data class SynchronizeNostrEventRequest(
|
||||
val id: String = Uuid.generateV4().toHexDashString(),
|
||||
val status: String = "pending",
|
||||
val relayURL: String,
|
||||
val eventIds: String? = null, // TODO: EventIdArray
|
||||
val authorPublicKeys: String? = null, // TODO: PublicKeyArray
|
||||
val kinds: String? = null, // TODO: KindArray
|
||||
val eventIds: HexArray? = null,
|
||||
val authorPublicKeys: HexArray? = null, // TODO: PublicKeyArray
|
||||
val kinds: KindArray? = null, // TODO: KindArray
|
||||
val tagName: String? = null,
|
||||
val since: Instant? = null,
|
||||
val until: Instant? = null,
|
||||
val limit: Long? = null,
|
||||
val limit: Int? = null,
|
||||
val search: String? = null,
|
||||
|
||||
override val nostrEventId: HexKey? = null,
|
||||
override val unsignedNostrEventId: Long? = null,
|
||||
override val createdAt: Instant = Clock.System.now(),
|
||||
override val updatedAt: Instant = createdAt,
|
||||
): OptionalNostrEventEntity, UnsignedNostrEventEntity, TimestampedEntity
|
||||
): OptionalNostrEventEntity, UnsignedNostrEventEntity, TimestampedEntity {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other == null || this::class != other::class) return false
|
||||
|
||||
other as SynchronizeNostrEventRequest
|
||||
|
||||
if (limit != other.limit) return false
|
||||
if (unsignedNostrEventId != other.unsignedNostrEventId) return false
|
||||
if (id != other.id) return false
|
||||
if (status != other.status) return false
|
||||
if (relayURL != other.relayURL) return false
|
||||
if (!eventIds.contentEquals(other.eventIds)) return false
|
||||
if (!authorPublicKeys.contentEquals(other.authorPublicKeys)) return false
|
||||
if (!kinds.contentEquals(other.kinds)) return false
|
||||
if (tagName != other.tagName) return false
|
||||
if (since != other.since) return false
|
||||
if (until != other.until) return false
|
||||
if (nostrEventId != other.nostrEventId) return false
|
||||
if (createdAt != other.createdAt) return false
|
||||
if (updatedAt != other.updatedAt) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = limit?.hashCode() ?: 0
|
||||
result = 31 * result + (unsignedNostrEventId?.hashCode() ?: 0)
|
||||
result = 31 * result + id.hashCode()
|
||||
result = 31 * result + status.hashCode()
|
||||
result = 31 * result + relayURL.hashCode()
|
||||
result = 31 * result + (eventIds?.contentHashCode() ?: 0)
|
||||
result = 31 * result + (authorPublicKeys?.contentHashCode() ?: 0)
|
||||
result = 31 * result + (kinds?.contentHashCode() ?: 0)
|
||||
result = 31 * result + (tagName?.hashCode() ?: 0)
|
||||
result = 31 * result + (since?.hashCode() ?: 0)
|
||||
result = 31 * result + (until?.hashCode() ?: 0)
|
||||
result = 31 * result + (nostrEventId?.hashCode() ?: 0)
|
||||
result = 31 * result + createdAt.hashCode()
|
||||
result = 31 * result + updatedAt.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package ac.aux.compose.database.model.typealiases
|
||||
|
||||
typealias HexArray = Array<String>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
package ac.aux.compose.database.model.typealiases
|
||||
|
||||
typealias KindArray = Array<Int>
|
||||
@@ -81,18 +81,19 @@ class DatabaseNostrRepository(
|
||||
// Look busy for the onboarding screen
|
||||
delay(3_500)
|
||||
|
||||
if (unsignedNostrEvent.signedAt != null) {
|
||||
// Submit a SyncNostrEventRequest
|
||||
synchronizationRelayURLs.forEach { relayUrl ->
|
||||
database.synchronizeNostrEventRequestDao().upsert(
|
||||
SynchronizeNostrEventRequest(
|
||||
authorPublicKeys = unsignedNostrEvent.pubKey,
|
||||
status = "pending",
|
||||
relayURL = relayUrl
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
// if (unsignedNostrEvent.signedAt != null) {
|
||||
// // Submit a SyncNostrEventRequest
|
||||
// synchronizationRelayURLs.forEach { relayUrl ->
|
||||
// database.synchronizeNostrEventRequestDao().upsert(
|
||||
// SynchronizeNostrEventRequest(
|
||||
// authorPublicKeys = arrayOf(unsignedNostrEvent.pubKey),
|
||||
// kinds = arrayOf(0),
|
||||
// status = "pending",
|
||||
// relayURL = relayUrl
|
||||
// )
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +190,8 @@ class DatabaseNostrRepository(
|
||||
|
||||
database.synchronizeNostrEventRequestDao().upsert(
|
||||
synchronizeNostrEventRequest.copy(
|
||||
nostrEventId = nostrEvent.id
|
||||
nostrEventId = nostrEvent.id,
|
||||
status = "processed"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -28,11 +28,13 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
fun UnqueuedProfileSynchronizationScreen(
|
||||
unsignedNostrEventId: Long,
|
||||
profilePublicKey: String,
|
||||
nostrRepository: NostrRepository
|
||||
) {
|
||||
val unqueuedProfileSynchronizationViewModel: UnqueuedProfileSynchronizationViewModel = viewModel(
|
||||
factory = UnqueuedProfileSynchronizationViewModel.factory(
|
||||
unsignedNostrEventId = unsignedNostrEventId,
|
||||
profilePublicKey = profilePublicKey,
|
||||
nostrRepository = nostrRepository
|
||||
)
|
||||
@@ -90,6 +92,7 @@ private fun UnqueuedProfileSynchronizationScreenPreview() {
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
UnqueuedProfileSynchronizationScreen(
|
||||
unsignedNostrEventId = 2,
|
||||
profilePublicKey = "npub is for living.",
|
||||
nostrRepository = NostrRepository.NO_OP_NOSTR_REPOSITORY
|
||||
)
|
||||
|
||||
@@ -132,7 +132,8 @@ fun AuxNavHost(
|
||||
is NavigationUIState.UnqueuedProfileSynchronization -> {
|
||||
navController.navigate(
|
||||
route = UnqueuedProfileSynchronizationRoute(
|
||||
state.unsignedNostrEvent.pubKey
|
||||
unsignedNostrEventId = state.unsignedNostrEvent.id,
|
||||
publicKey = state.unsignedNostrEvent.pubKey
|
||||
)
|
||||
) {
|
||||
popUpTo(0)
|
||||
@@ -255,6 +256,7 @@ fun AuxNavHost(
|
||||
composable<UnqueuedProfileSynchronizationRoute> { backStackEntry ->
|
||||
val route = backStackEntry.toRoute<UnqueuedProfileSynchronizationRoute>()
|
||||
UnqueuedProfileSynchronizationScreen(
|
||||
unsignedNostrEventId = route.unsignedNostrEventId,
|
||||
profilePublicKey = route.publicKey,
|
||||
nostrRepository = nostrRepository
|
||||
)
|
||||
|
||||
@@ -5,5 +5,6 @@ import kotlinx.serialization.Serializable
|
||||
@Serializable
|
||||
data class UnqueuedProfileSynchronizationRoute(
|
||||
val publicKey: String,
|
||||
val unsignedNostrEventId: Long,
|
||||
): Route() {
|
||||
}
|
||||
@@ -12,7 +12,10 @@ import androidx.lifecycle.viewmodel.initializer
|
||||
import androidx.lifecycle.viewmodel.viewModelFactory
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.core.OptimizedJsonMapper
|
||||
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.ReqCmd
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync
|
||||
import com.vitorpamplona.quartz.utils.text
|
||||
import io.ktor.client.HttpClient
|
||||
@@ -34,9 +37,7 @@ import kotlinx.coroutines.flow.getAndUpdate
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlin.collections.set
|
||||
import kotlin.time.Instant
|
||||
|
||||
@@ -91,7 +92,7 @@ class NavigationViewModel(
|
||||
observeUnsignedNostrEvents()
|
||||
observePendingBroadcastNostrEventRequests()
|
||||
observeProfile()
|
||||
observeSyncNostrEventRequests()
|
||||
observePendingSyncNostrEventRequests()
|
||||
}
|
||||
|
||||
|
||||
@@ -133,118 +134,123 @@ class NavigationViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeSyncNostrEventRequests() {
|
||||
logger.i { "observeSyncNostrEventRequests" }
|
||||
private fun observePendingSyncNostrEventRequests() {
|
||||
logger.i { "observePendingSyncNostrEventRequests" }
|
||||
|
||||
scope.launch(Dispatchers.IO) {
|
||||
nostrRepository.observePendingSynchronizeNostrEventRequests().collect { synchronizeNostrEventRequestOrNull ->
|
||||
synchronizeNostrEventRequestOrNull?.let { synchronizeNostrEventRequest ->
|
||||
val filterContent = mutableMapOf<String, JsonElement>()
|
||||
|
||||
synchronizeNostrEventRequest.eventIds?.let { eventIds ->
|
||||
filterContent.put("ids", JsonPrimitive(eventIds))
|
||||
}
|
||||
synchronizeNostrEventRequest.authorPublicKeys?.let { authorPublicKeys ->
|
||||
filterContent.put("authors", JsonPrimitive(authorPublicKeys))
|
||||
}
|
||||
synchronizeNostrEventRequest.kinds?.let { kinds ->
|
||||
filterContent.put("kinds", JsonPrimitive(kinds))
|
||||
}
|
||||
synchronizeNostrEventRequest.since?.let { since ->
|
||||
filterContent.put("since", JsonPrimitive(since.toEpochMilliseconds()))
|
||||
}
|
||||
synchronizeNostrEventRequest.until?.let { until ->
|
||||
filterContent.put("until", JsonPrimitive(until.toEpochMilliseconds()))
|
||||
}
|
||||
synchronizeNostrEventRequest.limit?.let { limit ->
|
||||
filterContent.put("limit", JsonPrimitive(limit))
|
||||
}
|
||||
|
||||
val filter = JsonObject(
|
||||
filterContent
|
||||
)
|
||||
logger.d("Syncing: $filter")
|
||||
scope.launch {
|
||||
val webSocketSession = httpClient.webSocketSession(
|
||||
urlString = synchronizeNostrEventRequest.relayURL
|
||||
try {
|
||||
val reqCommand = ReqCmd(
|
||||
subId = synchronizeNostrEventRequest.id,
|
||||
filters = listOf(
|
||||
Filter(
|
||||
ids = synchronizeNostrEventRequest.eventIds?.map { eventId -> eventId },
|
||||
authors = synchronizeNostrEventRequest.authorPublicKeys?.map { authorPublicKey -> authorPublicKey },
|
||||
kinds = synchronizeNostrEventRequest.kinds?.map { kind -> kind },
|
||||
since = synchronizeNostrEventRequest.since?.toEpochMilliseconds(),
|
||||
until = synchronizeNostrEventRequest.until?.toEpochMilliseconds(),
|
||||
limit = synchronizeNostrEventRequest.limit,
|
||||
search = synchronizeNostrEventRequest.search
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
syncingJobs[synchronizeNostrEventRequest.relayURL] = scope.launch {
|
||||
webSocketSession.send(
|
||||
frame = Frame.Text("[\"REQ\",${synchronizeNostrEventRequest.id},${filter}]")
|
||||
scope.launch {
|
||||
val webSocketSession = httpClient.webSocketSession(
|
||||
urlString = synchronizeNostrEventRequest.relayURL
|
||||
)
|
||||
|
||||
nostrRepository.synchronizeNostrEventRequestProcessed(synchronizeNostrEventRequest)
|
||||
syncingJobs[synchronizeNostrEventRequest.relayURL] = scope.launch {
|
||||
val filterRequest = OptimizedJsonMapper.toJson(reqCommand)
|
||||
logger.d("Syncing: $filterRequest")
|
||||
webSocketSession.send(
|
||||
frame = Frame.Text(filterRequest)
|
||||
)
|
||||
|
||||
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)
|
||||
nostrRepository.synchronizeNostrEventRequestProcessed(synchronizeNostrEventRequest)
|
||||
|
||||
if (result.firstOrNull()?.text == "EVENT") {
|
||||
result.getOrNull(1)?.text?.let { subscriptionId ->
|
||||
if (subscriptionId == synchronizeNostrEventRequest.id) {
|
||||
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)
|
||||
|
||||
result.getOrNull(2)?.text?.let { jsonText ->
|
||||
val event = Event.fromJson(
|
||||
jsonText
|
||||
)
|
||||
if (result.firstOrNull()?.text == "EVENT") {
|
||||
result.getOrNull(1)?.text?.let { subscriptionId ->
|
||||
if (subscriptionId == synchronizeNostrEventRequest.id) {
|
||||
|
||||
val nostrEvent = NostrEvent(
|
||||
id = event.id,
|
||||
pubKey = event.pubKey,
|
||||
kind = event.kind,
|
||||
content = event.content,
|
||||
tags = event.tags,
|
||||
createdAt = Instant.fromEpochMilliseconds(event.createdAt),
|
||||
sig = event.sig
|
||||
)
|
||||
result.getOrNull(2)?.jsonObject?.toString()?.let { jsonText ->
|
||||
logger.i("Processing: $jsonText")
|
||||
val event = Event.fromJson(
|
||||
jsonText
|
||||
)
|
||||
|
||||
nostrRepository.saveNostrEvent(
|
||||
nostrEvent = nostrEvent,
|
||||
synchronizeNostrEventRequest,
|
||||
synchronizationRelayURLs = Relays.eventPublishRelaySet.map { normalizedRelayUrl -> normalizedRelayUrl.url }
|
||||
)
|
||||
val nostrEvent = NostrEvent(
|
||||
id = event.id,
|
||||
pubKey = event.pubKey,
|
||||
kind = event.kind,
|
||||
content = event.content,
|
||||
tags = event.tags,
|
||||
createdAt = Instant.fromEpochMilliseconds(event.createdAt),
|
||||
unsignedNostrEventId = synchronizeNostrEventRequest.unsignedNostrEventId,
|
||||
sig = event.sig
|
||||
)
|
||||
|
||||
nostrRepository.saveNostrEvent(
|
||||
nostrEvent = nostrEvent,
|
||||
synchronizeNostrEventRequest,
|
||||
synchronizationRelayURLs = Relays.eventPublishRelaySet.map { normalizedRelayUrl -> normalizedRelayUrl.url }
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
} 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")
|
||||
}
|
||||
} else {
|
||||
logger.d("Unsupported: $text")
|
||||
} catch (e: Throwable) {
|
||||
logger.e("Error processing receipt: $text", e)
|
||||
}
|
||||
} 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")
|
||||
}
|
||||
// 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")
|
||||
}
|
||||
|
||||
logger.i("Websocket exit")
|
||||
syncingJobs[synchronizeNostrEventRequest.relayURL]?.join()
|
||||
}
|
||||
|
||||
syncingJobs[synchronizeNostrEventRequest.relayURL]?.join()
|
||||
} catch (e: Throwable) {
|
||||
logger.e("Relay Error", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,11 @@ import com.vitorpamplona.quartz.nip19Bech32.entities.NPub
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NSec
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.IO
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class UnqueuedProfileSynchronizationViewModel(
|
||||
val unsignedNostrEventId: Long,
|
||||
val profilePublicKey: String,
|
||||
val nostrRepository: NostrRepository
|
||||
): ViewModel() {
|
||||
@@ -29,11 +31,13 @@ class UnqueuedProfileSynchronizationViewModel(
|
||||
private const val TAG = "UnqueuedProfileSynchronizationViewModel"
|
||||
|
||||
fun factory(
|
||||
unsignedNostrEventId: Long,
|
||||
profilePublicKey: String,
|
||||
nostrRepository: NostrRepository
|
||||
): ViewModelProvider.Factory = viewModelFactory {
|
||||
initializer {
|
||||
UnqueuedProfileSynchronizationViewModel(
|
||||
unsignedNostrEventId = unsignedNostrEventId,
|
||||
profilePublicKey = profilePublicKey,
|
||||
nostrRepository = nostrRepository
|
||||
)
|
||||
@@ -49,11 +53,14 @@ class UnqueuedProfileSynchronizationViewModel(
|
||||
logger.d { "queueSynchronization" }
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
|
||||
delay(2_000)
|
||||
|
||||
nostrRepository.queueSynchronizeNostrEvent(
|
||||
Relays.eventPublishRelaySet.map { normalizedRelayUrl ->
|
||||
SynchronizeNostrEventRequest(
|
||||
authorPublicKeys = profilePublicKey,
|
||||
kinds = "0",
|
||||
authorPublicKeys = arrayOf(profilePublicKey),
|
||||
unsignedNostrEventId = unsignedNostrEventId,
|
||||
kinds = arrayOf(0),
|
||||
relayURL = normalizedRelayUrl.url
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user