More debugging information

This commit is contained in:
Vitor Pamplona
2026-03-19 09:34:52 -04:00
parent 3e3adaff10
commit ec29a47dc4
4 changed files with 179 additions and 94 deletions
@@ -38,7 +38,6 @@ import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.ReqCmd
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip01Core.tags.people.isTaggedUser import com.vitorpamplona.quartz.nip01Core.tags.people.isTaggedUser
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@@ -50,8 +49,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.supervisorScope import kotlinx.coroutines.supervisorScope
import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.Semaphore
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicInteger import kotlin.coroutines.cancellation.CancellationException
import java.util.concurrent.atomic.AtomicLong
/** /**
* Syncs the user's events across all known relays: * Syncs the user's events across all known relays:
@@ -103,12 +101,24 @@ class EventSync(
object Idle : SyncState() object Idle : SyncState()
data class Running( data class Running(
val relaysCompleted: Int, val relaysCompleted: MutableStateFlow<Int>,
val totalRelays: Int, val totalRelays: MutableStateFlow<Int>,
val eventsSent: Int, val eventsSent: MutableStateFlow<Int>,
) : SyncState() val eventsReceived: MutableStateFlow<Int>,
val eventsAccepted: MutableStateFlow<Int>,
) : SyncState() {
constructor(relaysCompleted: Int, totalRelays: Int, eventsSent: Int, eventsReceived: Int, eventsAccepted: Int) :
this(
relaysCompleted = MutableStateFlow(relaysCompleted),
totalRelays = MutableStateFlow(totalRelays),
eventsSent = MutableStateFlow(eventsSent),
eventsReceived = MutableStateFlow(eventsReceived),
eventsAccepted = MutableStateFlow(eventsAccepted),
)
}
data class Done( data class Done(
val totalEventsReceived: Int,
val totalEventsSent: Int, val totalEventsSent: Int,
val totalEventsAccepted: Int, val totalEventsAccepted: Int,
val durationMs: Long, val durationMs: Long,
@@ -180,9 +190,16 @@ class EventSync(
val status: MutableStateFlow<ConnectionStatus>, val status: MutableStateFlow<ConnectionStatus>,
val eventsFound: MutableStateFlow<Int>, val eventsFound: MutableStateFlow<Int>,
val eventsAccepted: MutableStateFlow<Int>, val eventsAccepted: MutableStateFlow<Int>,
val pageUntil: MutableStateFlow<Long?> = MutableStateFlow(null),
) { ) {
constructor(relay: NormalizedRelayUrl, status: ConnectionStatus, eventsFound: Int, eventsAccepted: Int) : constructor(relay: NormalizedRelayUrl, status: ConnectionStatus, eventsFound: Int, eventsAccepted: Int, untilPage: Long? = null) :
this(relay, MutableStateFlow(status), MutableStateFlow(eventsFound), MutableStateFlow(eventsAccepted)) this(
relay = relay,
status = MutableStateFlow(status),
eventsFound = MutableStateFlow(eventsFound),
eventsAccepted = MutableStateFlow(eventsAccepted),
pageUntil = MutableStateFlow(untilPage),
)
} }
/** /**
@@ -346,14 +363,17 @@ class EventSync(
val outboxDedup = ConcurrentHashMap.newKeySet<String>() val outboxDedup = ConcurrentHashMap.newKeySet<String>()
val inboxDedup = ConcurrentHashMap.newKeySet<String>() val inboxDedup = ConcurrentHashMap.newKeySet<String>()
val dmDedup = ConcurrentHashMap.newKeySet<String>() val dmDedup = ConcurrentHashMap.newKeySet<String>()
val totalSent = AtomicLong(0)
// OK (true) tracking: maps each sent event ID to its source relay.
// The first OK true for an event atomically removes it from this map,
// crediting the acceptance to the source relay and preventing double-counting.
val sourceRelayOfEvent = ConcurrentHashMap<HexKey, NormalizedRelayUrl>() val sourceRelayOfEvent = ConcurrentHashMap<HexKey, NormalizedRelayUrl>()
val acceptedCountPerSourceRelay = ConcurrentHashMap<NormalizedRelayUrl, AtomicInteger>()
val totalAccepted = AtomicLong(0) val runningState =
SyncState.Running(
relaysCompleted = 0,
totalRelays = totalRelays,
eventsSent = 0,
eventsReceived = 0,
eventsAccepted = 0,
)
val okListener = val okListener =
object : IRelayClientListener { object : IRelayClientListener {
@@ -375,22 +395,30 @@ class EventSync(
success: Boolean, success: Boolean,
) { ) {
super.onSent(relay, cmdStr, cmd, success) super.onSent(relay, cmdStr, cmd, success)
if (cmd is EventCmd) { if (cmd is EventCmd) {
var hasSent = false
if (outboxDedup.contains(cmd.event.id)) { if (outboxDedup.contains(cmd.event.id)) {
liveActivity.value.outboxTargets[relay.url] liveActivity.value.outboxTargets[relay.url]
?.eventsSent ?.eventsSent
?.update { it + 1 } ?.update { it + 1 }
hasSent = true
} }
if (inboxDedup.contains(cmd.event.id)) { if (inboxDedup.contains(cmd.event.id)) {
liveActivity.value.inboxTargets[relay.url] liveActivity.value.inboxTargets[relay.url]
?.eventsSent ?.eventsSent
?.update { it + 1 } ?.update { it + 1 }
hasSent = true
} }
if (dmDedup.contains(cmd.event.id)) { if (dmDedup.contains(cmd.event.id)) {
liveActivity.value.dmTargets[relay.url] liveActivity.value.dmTargets[relay.url]
?.eventsSent ?.eventsSent
?.update { it + 1 } ?.update { it + 1 }
hasSent = true
}
if (hasSent) {
runningState.eventsSent.update { it + 1 }
} }
} else if (cmd is ReqCmd) { } else if (cmd is ReqCmd) {
val currentStatus = liveActivity.value.runningRelays[relay.url]?.status val currentStatus = liveActivity.value.runningRelays[relay.url]?.status
@@ -407,37 +435,39 @@ class EventSync(
) { ) {
if (msg is OkMessage && msg.success && msg.message.isBlank()) { if (msg is OkMessage && msg.success && msg.message.isBlank()) {
// remove() is atomic: returns non-null only for the first OK per event. // remove() is atomic: returns non-null only for the first OK per event.
val sourceRelay = sourceRelayOfEvent.remove(msg.eventId) ?: return val sourceRelay = sourceRelayOfEvent.remove(msg.eventId)
acceptedCountPerSourceRelay.getOrPut(sourceRelay) { AtomicInteger(0) }.incrementAndGet() if (sourceRelay != null) {
totalAccepted.incrementAndGet() liveActivity.value.runningRelays[sourceRelay]
?.eventsAccepted
?.update { it + 1 }
}
if (outboxDedup.contains(msg.eventId)) { if (outboxDedup.contains(msg.eventId)) {
liveActivity.value.outboxTargets[relay.url] val relayTarget = liveActivity.value.outboxTargets[relay.url]
?.eventsAccepted if (relayTarget != null) {
?.update { it + 1 } relayTarget.eventsAccepted.update { it + 1 }
runningState.eventsAccepted.update { it + 1 }
}
} }
if (dmDedup.contains(msg.eventId)) { if (dmDedup.contains(msg.eventId)) {
liveActivity.value.dmTargets[relay.url] val relayTarget = liveActivity.value.dmTargets[relay.url]
?.eventsAccepted if (relayTarget != null) {
?.update { it + 1 } relayTarget.eventsAccepted.update { it + 1 }
runningState.eventsAccepted.update { it + 1 }
}
} }
if (inboxDedup.contains(msg.eventId)) { if (inboxDedup.contains(msg.eventId)) {
liveActivity.value.inboxTargets[relay.url] val relayTarget = liveActivity.value.inboxTargets[relay.url]
?.eventsAccepted if (relayTarget != null) {
?.update { it + 1 } relayTarget.eventsAccepted.update { it + 1 }
runningState.eventsAccepted.update { it + 1 }
}
} }
} }
} }
} }
val relaysCompleted = AtomicInteger(0) _syncState.emit(runningState)
_syncState.value =
SyncState.Running(
relaysCompleted = relaysCompleted.get(),
totalRelays = totalRelays,
eventsSent = totalSent.get().toInt(),
)
clientBuilder().use { client -> clientBuilder().use { client ->
client.subscribe(okListener) client.subscribe(okListener)
@@ -445,6 +475,11 @@ class EventSync(
client.downloadFromPool( client.downloadFromPool(
relays = relaysToProcess, relays = relaysToProcess,
filters = perRelayFilters, filters = perRelayFilters,
onNewPage = { until, sourceRelay ->
_liveActivity.value.runningRelays[sourceRelay]
?.pageUntil
?.tryEmit(until)
},
onEvent = { event, sourceRelay -> onEvent = { event, sourceRelay ->
val isMyEvent = event.pubKey == myPubKey val isMyEvent = event.pubKey == myPubKey
val mentionsMe = event.tags.isTaggedUser(myPubKey) val mentionsMe = event.tags.isTaggedUser(myPubKey)
@@ -452,36 +487,41 @@ class EventSync(
val live = liveActivity.value val live = liveActivity.value
var newEvent = false
var matchesAtLeastOneFilter = false
// Each routing rule is independent: an event can match more than one. // Each routing rule is independent: an event can match more than one.
if (isMyEvent && outboxTargets.isNotEmpty()) { if (isMyEvent && outboxTargets.isNotEmpty()) {
if (outboxDedup.add(event.id)) { if (outboxDedup.add(event.id)) {
sourceRelayOfEvent[event.id] = sourceRelay
client.send(event, outboxTargets) client.send(event, outboxTargets)
totalSent.incrementAndGet() newEvent = true
live.runningRelays[sourceRelay]?.eventsFound?.update { it + 1 }
live.completedRelays[sourceRelay]?.eventsFound?.update { it + 1 }
} }
matchesAtLeastOneFilter = true
} }
if (mentionsMe && isDmKind && dmTargets.isNotEmpty()) { if (mentionsMe && isDmKind && dmTargets.isNotEmpty()) {
if (dmDedup.add(event.id)) { if (dmDedup.add(event.id)) {
sourceRelayOfEvent[event.id] = sourceRelay
client.send(event, dmTargets) client.send(event, dmTargets)
totalSent.incrementAndGet() newEvent = true
live.runningRelays[sourceRelay]?.eventsFound?.update { it + 1 }
live.completedRelays[sourceRelay]?.eventsFound?.update { it + 1 }
} }
matchesAtLeastOneFilter = true
} }
if (mentionsMe && !isDmKind && inboxTargets.isNotEmpty()) { if (mentionsMe && !isDmKind && inboxTargets.isNotEmpty()) {
if (inboxDedup.add(event.id)) { if (inboxDedup.add(event.id)) {
sourceRelayOfEvent[event.id] = sourceRelay
client.send(event, inboxTargets) client.send(event, inboxTargets)
totalSent.incrementAndGet() newEvent = true
live.runningRelays[sourceRelay]?.eventsFound?.update { it + 1 }
live.completedRelays[sourceRelay]?.eventsFound?.update { it + 1 }
} }
matchesAtLeastOneFilter = true
}
if (newEvent) {
sourceRelayOfEvent[event.id] = sourceRelay
}
if (matchesAtLeastOneFilter) {
runningState.eventsReceived.update { it + 1 }
live.runningRelays[sourceRelay]?.eventsFound?.update { it + 1 }
live.completedRelays[sourceRelay]?.eventsFound?.update { it + 1 }
} }
}, },
onRelayStart = { relay -> onRelayStart = { relay ->
@@ -513,30 +553,27 @@ class EventSync(
status?.tryEmit(LiveSyncActivity.ConnectionStatus.Completed) status?.tryEmit(LiveSyncActivity.ConnectionStatus.Completed)
} }
_syncState.value = runningState.relaysCompleted.update { it + 1 }
SyncState.Running(
relaysCompleted = relaysCompleted.incrementAndGet(),
totalRelays = totalRelays,
eventsSent = totalSent.get().toInt(),
)
}, },
) )
_syncState.value = _syncState.value =
SyncState.Done( SyncState.Done(
totalEventsSent = totalSent.get().toInt(), totalEventsReceived = runningState.eventsReceived.value,
totalEventsAccepted = totalAccepted.get().toInt(), totalEventsSent = runningState.eventsSent.value,
totalEventsAccepted = runningState.eventsAccepted.value,
durationMs = System.currentTimeMillis() - startTime, durationMs = System.currentTimeMillis() - startTime,
) )
} catch (e: CancellationException) { } catch (e: Exception) {
_syncState.value = _syncState.value =
SyncState.Done( SyncState.Done(
totalEventsSent = totalSent.get().toInt(), totalEventsReceived = runningState.eventsReceived.value,
totalEventsAccepted = totalAccepted.get().toInt(), totalEventsSent = runningState.eventsSent.value,
totalEventsAccepted = runningState.eventsAccepted.value,
durationMs = System.currentTimeMillis() - startTime, durationMs = System.currentTimeMillis() - startTime,
) )
throw e
} catch (e: Exception) { if (e is CancellationException) throw e
_syncState.value = SyncState.Error(e.message ?: "Unknown error") _syncState.value = SyncState.Error(e.message ?: "Unknown error")
} finally { } finally {
client.unsubscribe(okListener) client.unsubscribe(okListener)
@@ -554,6 +591,7 @@ class EventSync(
private suspend fun INostrClient.downloadFromPool( private suspend fun INostrClient.downloadFromPool(
relays: List<NormalizedRelayUrl>, relays: List<NormalizedRelayUrl>,
filters: Map<NormalizedRelayUrl, List<Filter>>, filters: Map<NormalizedRelayUrl, List<Filter>>,
onNewPage: (Long, NormalizedRelayUrl) -> Unit,
onEvent: (Event, NormalizedRelayUrl) -> Unit, onEvent: (Event, NormalizedRelayUrl) -> Unit,
onRelayStart: (NormalizedRelayUrl) -> Unit, onRelayStart: (NormalizedRelayUrl) -> Unit,
onRelayComplete: (NormalizedRelayUrl) -> Unit, onRelayComplete: (NormalizedRelayUrl) -> Unit,
@@ -567,7 +605,12 @@ class EventSync(
try { try {
onRelayStart(relay) onRelayStart(relay)
filters[relay]?.let { filtersForRelay -> filters[relay]?.let { filtersForRelay ->
downloadFromRelay(relay, filtersForRelay) { event -> onEvent(event, relay) } downloadFromRelay(
relay = relay,
filters = filtersForRelay,
onNewPage = { onNewPage(it, relay) },
onEvent = { onEvent(it, relay) },
)
} ?: 0 } ?: 0
onRelayComplete(relay) onRelayComplete(relay)
} finally { } finally {
@@ -587,6 +630,7 @@ class EventSync(
private suspend fun INostrClient.downloadFromRelay( private suspend fun INostrClient.downloadFromRelay(
relay: NormalizedRelayUrl, relay: NormalizedRelayUrl,
filters: List<Filter>, filters: List<Filter>,
onNewPage: (Long) -> Unit,
onEvent: (Event) -> Unit, onEvent: (Event) -> Unit,
): Int = reqBypassingRelayLimits(relay, filters, RELAY_TIMEOUT_MS, onEvent) ): Int = reqBypassingRelayLimits(relay, filters, RELAY_TIMEOUT_MS, onNewPage, onEvent)
} }
@@ -54,6 +54,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
@@ -63,6 +64,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarWithBackButton import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarWithBackButton
import com.vitorpamplona.amethyst.ui.note.timeAgoNoDot
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonColumn import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonColumn
@@ -292,28 +294,9 @@ private fun SyncProgressCard(
elevation = CardDefaults.cardElevation(defaultElevation = 1.dp), elevation = CardDefaults.cardElevation(defaultElevation = 1.dp),
) { ) {
Column(modifier = Modifier.padding(16.dp)) { Column(modifier = Modifier.padding(16.dp)) {
Text( RelayStatement(state)
text = stringRes(R.string.event_sync_relays_progress, state.relaysCompleted, state.totalRelays),
style = MaterialTheme.typography.titleSmall,
fontWeight = FontWeight.SemiBold,
)
Spacer(Modifier.height(8.dp)) Spacer(Modifier.height(8.dp))
LinearProgressIndicator( EventsReceivedStatement(state)
progress = {
if (state.totalRelays > 0) {
state.relaysCompleted.toFloat() / state.totalRelays
} else {
0f
}
},
modifier = Modifier.fillMaxWidth(),
)
Spacer(Modifier.height(8.dp))
Text(
text = stringRes(R.string.event_sync_events_sent, state.eventsSent),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
Spacer(Modifier.height(10.dp)) Spacer(Modifier.height(10.dp))
OutlinedButton( OutlinedButton(
onClick = onCancel, onClick = onCancel,
@@ -329,6 +312,42 @@ private fun SyncProgressCard(
} }
} }
@Composable
private fun EventsReceivedStatement(state: EventSync.SyncState.Running) {
val eventsReceived by state.eventsReceived.collectAsStateWithLifecycle()
val eventsSent by state.eventsSent.collectAsStateWithLifecycle()
val eventsAccepted by state.eventsAccepted.collectAsStateWithLifecycle()
Text(
text = stringRes(R.string.event_sync_events_sent, eventsAccepted, eventsSent, eventsReceived),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
@Composable
private fun RelayStatement(state: EventSync.SyncState.Running) {
val relaysCompleted by state.relaysCompleted.collectAsStateWithLifecycle()
val totalRelays by state.totalRelays.collectAsStateWithLifecycle()
Text(
text = stringRes(R.string.event_sync_relays_progress, relaysCompleted, totalRelays),
style = MaterialTheme.typography.titleSmall,
fontWeight = FontWeight.SemiBold,
)
Spacer(Modifier.height(8.dp))
LinearProgressIndicator(
progress = {
if (totalRelays > 0) {
relaysCompleted / totalRelays.toFloat()
} else {
0f
}
},
modifier = Modifier.fillMaxWidth(),
)
}
@Composable @Composable
private fun DoneCard( private fun DoneCard(
state: EventSync.SyncState.Done, state: EventSync.SyncState.Done,
@@ -351,7 +370,7 @@ private fun DoneCard(
) )
Spacer(Modifier.height(6.dp)) Spacer(Modifier.height(6.dp))
Text( Text(
text = stringRes(R.string.event_sync_done_sent, state.totalEventsSent), text = stringRes(R.string.event_sync_done_sent, state.totalEventsSent, state.totalEventsReceived),
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onPrimaryContainer, color = MaterialTheme.colorScheme.onPrimaryContainer,
) )
@@ -544,7 +563,6 @@ private fun DestinationRelayRow(
@Composable @Composable
private fun ActivityLogRow(info: EventSync.LiveSyncActivity.SourceRelayInfo) { private fun ActivityLogRow(info: EventSync.LiveSyncActivity.SourceRelayInfo) {
val eventsFound by info.eventsFound.collectAsStateWithLifecycle() val eventsFound by info.eventsFound.collectAsStateWithLifecycle()
val status by info.status.collectAsStateWithLifecycle()
val hasEvents = eventsFound > 0 val hasEvents = eventsFound > 0
val dotColor = val dotColor =
if (hasEvents) { if (hasEvents) {
@@ -583,12 +601,25 @@ private fun ActivityLogRow(info: EventSync.LiveSyncActivity.SourceRelayInfo) {
modifier = Modifier.weight(0.6f), modifier = Modifier.weight(0.6f),
) )
if (hasEvents) { if (hasEvents) {
val context = LocalContext.current
val untilPage by info.pageUntil.collectAsStateWithLifecycle()
val eventsAccepted by info.eventsAccepted.collectAsStateWithLifecycle() val eventsAccepted by info.eventsAccepted.collectAsStateWithLifecycle()
untilPage?.let {
Text(
text = stringRes(R.string.event_sync_less_than_until, timeAgoNoDot(it, context)),
style = MaterialTheme.typography.bodySmall,
color = textColor,
modifier = Modifier.weight(0.3f),
maxLines = 1,
textAlign = TextAlign.End,
overflow = TextOverflow.StartEllipsis,
)
}
Text( Text(
text = stringRes(R.string.event_sync_log_recv, formatCount(eventsFound)), text = stringRes(R.string.event_sync_log_recv, formatCount(eventsFound)),
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = textColor, color = textColor,
modifier = Modifier.weight(0.2f), modifier = Modifier.weight(0.3f),
maxLines = 1, maxLines = 1,
textAlign = TextAlign.End, textAlign = TextAlign.End,
overflow = TextOverflow.StartEllipsis, overflow = TextOverflow.StartEllipsis,
@@ -606,7 +637,7 @@ private fun ActivityLogRow(info: EventSync.LiveSyncActivity.SourceRelayInfo) {
maxLines = 1, maxLines = 1,
textAlign = TextAlign.End, textAlign = TextAlign.End,
overflow = TextOverflow.StartEllipsis, overflow = TextOverflow.StartEllipsis,
modifier = Modifier.weight(0.2f), modifier = Modifier.weight(0.3f),
) )
} else { } else {
val status by info.status.collectAsStateWithLifecycle() val status by info.status.collectAsStateWithLifecycle()
@@ -737,7 +768,9 @@ fun SyncProgressCardPreview() {
EventSync.SyncState.Running( EventSync.SyncState.Running(
relaysCompleted = 312, relaysCompleted = 312,
totalRelays = 1024, totalRelays = 1024,
eventsAccepted = 12,
eventsSent = 4821, eventsSent = 4821,
eventsReceived = 10000,
), ),
onCancel = {}, onCancel = {},
) )
@@ -751,6 +784,7 @@ fun DoneCardPreview() {
DoneCard( DoneCard(
state = state =
EventSync.SyncState.Done( EventSync.SyncState.Done(
totalEventsReceived = 20_000,
totalEventsSent = 18_432, totalEventsSent = 18_432,
totalEventsAccepted = 14_891, totalEventsAccepted = 14_891,
durationMs = 187_000, durationMs = 187_000,
@@ -791,7 +825,7 @@ fun EventScreenBodyPreview() {
fun EventScreenBody2Preview() { fun EventScreenBody2Preview() {
ThemeComparisonRow { ThemeComparisonRow {
EventScreenBody( EventScreenBody(
EventSync.SyncState.Running(1247, 1024, 4821), EventSync.SyncState.Running(1047, 1224, 100, 4821, 10000),
previewActivity, previewActivity,
) )
} }
+3 -2
View File
@@ -1829,11 +1829,11 @@
<string name="event_sync_start_over">Start Over</string> <string name="event_sync_start_over">Start Over</string>
<string name="event_sync_cancel">Cancel</string> <string name="event_sync_cancel">Cancel</string>
<string name="event_sync_relays_progress">Relays: %1$d / %2$d</string> <string name="event_sync_relays_progress">Relays: %1$d / %2$d</string>
<string name="event_sync_events_sent">Events redistributed: %1$d</string> <string name="event_sync_events_sent">Events redistributed: %1$d new out of %2$d sent and %3$d received</string>
<string name="event_sync_paused_title">Sync Paused</string> <string name="event_sync_paused_title">Sync Paused</string>
<string name="event_sync_paused_body">Completed %1$d of %2$d relays — %3$d events redistributed so far. Tap Resume to continue.</string> <string name="event_sync_paused_body">Completed %1$d of %2$d relays — %3$d events redistributed so far. Tap Resume to continue.</string>
<string name="event_sync_done_title">Sync complete</string> <string name="event_sync_done_title">Sync complete</string>
<string name="event_sync_done_sent">Forwarded %1$d events to destination relays.</string> <string name="event_sync_done_sent">Forwarded %1$d events to destination relays out of %2$d received.</string>
<string name="event_sync_done_accepted">%1$d events accepted as new by destination relays.</string> <string name="event_sync_done_accepted">%1$d events accepted as new by destination relays.</string>
<string name="event_sync_done_duration">Completed in %1$d seconds.</string> <string name="event_sync_done_duration">Completed in %1$d seconds.</string>
<string name="event_sync_error_title">Sync error</string> <string name="event_sync_error_title">Sync error</string>
@@ -1854,6 +1854,7 @@
<string name="relay_settings_lower">relay settings</string> <string name="relay_settings_lower">relay settings</string>
<string name="last_seen">Last seen %1$s ago</string> <string name="last_seen">Last seen %1$s ago</string>
<string name="event_sync_less_than_until">&lt;%1$s</string>
<string name="event_sync_status_connecting">Connecting</string> <string name="event_sync_status_connecting">Connecting</string>
<string name="event_sync_status_downloading">Downloading</string> <string name="event_sync_status_downloading">Downloading</string>
<string name="event_sync_status_error">Error</string> <string name="event_sync_status_error">Error</string>
@@ -21,6 +21,8 @@
package com.vitorpamplona.quartz.nip01Core.relay.client.accessories package com.vitorpamplona.quartz.nip01Core.relay.client.accessories
import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.relay.client.EmptyNostrClient.close
import com.vitorpamplona.quartz.nip01Core.relay.client.EmptyNostrClient.openReqSubscription
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.IRequestListener import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.IRequestListener
import com.vitorpamplona.quartz.nip01Core.relay.client.single.newSubId import com.vitorpamplona.quartz.nip01Core.relay.client.single.newSubId
@@ -54,6 +56,7 @@ suspend fun INostrClient.reqBypassingRelayLimits(
relay: NormalizedRelayUrl, relay: NormalizedRelayUrl,
filters: List<Filter>, filters: List<Filter>,
timeoutMs: Long = 30_000L, timeoutMs: Long = 30_000L,
onNewPage: ((Long) -> Unit)? = null,
onEvent: (Event) -> Unit, onEvent: (Event) -> Unit,
): Int { ): Int {
var until: Long? = null var until: Long? = null
@@ -82,6 +85,7 @@ suspend fun INostrClient.reqBypassingRelayLimits(
if (until == null) { if (until == null) {
remainingFilters remainingFilters
} else { } else {
onNewPage?.invoke(until)
remainingFilters.map { it.copy(until = until) } remainingFilters.map { it.copy(until = until) }
} }
@@ -162,11 +166,13 @@ suspend fun INostrClient.reqBypassingRelayLimits(
relay: String, relay: String,
filters: List<Filter>, filters: List<Filter>,
timeoutMs: Long = 30_000L, timeoutMs: Long = 30_000L,
onNewPage: ((Long) -> Unit)? = null,
onEvent: (Event) -> Unit, onEvent: (Event) -> Unit,
): Int = ): Int =
reqBypassingRelayLimits( reqBypassingRelayLimits(
relay = RelayUrlNormalizer.normalize(relay), relay = RelayUrlNormalizer.normalize(relay),
filters = filters, filters = filters,
timeoutMs = timeoutMs, timeoutMs = timeoutMs,
onNewPage = onNewPage,
onEvent = onEvent, onEvent = onEvent,
) )