Improves vanishing tests
This commit is contained in:
+23
-21
@@ -65,6 +65,8 @@ import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarWithBackButton
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.displayUrl
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
@@ -79,7 +81,6 @@ fun VanishEventsScreen(
|
||||
|
||||
val vanishEvents by viewModel.vanishEvents.collectAsStateWithLifecycle()
|
||||
val isLoading by viewModel.isLoading.collectAsStateWithLifecycle()
|
||||
val complianceResults by viewModel.complianceResults.collectAsStateWithLifecycle()
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.load()
|
||||
@@ -143,9 +144,8 @@ fun VanishEventsScreen(
|
||||
items(vanishEvents, key = { it.event.id }) { item ->
|
||||
VanishEventCard(
|
||||
item = item,
|
||||
complianceResults = complianceResults,
|
||||
onTestCompliance = { relayUrl, date ->
|
||||
viewModel.testCompliance(relayUrl, date)
|
||||
onTestCompliance = { relay ->
|
||||
viewModel.testCompliance(item, relay)
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -160,8 +160,7 @@ fun VanishEventsScreen(
|
||||
@Composable
|
||||
private fun VanishEventCard(
|
||||
item: VanishEventItem,
|
||||
complianceResults: Map<String, ComplianceStatus>,
|
||||
onTestCompliance: (String, Long) -> Unit,
|
||||
onTestCompliance: (NormalizedRelayUrl) -> Unit,
|
||||
) {
|
||||
Card(
|
||||
modifier =
|
||||
@@ -230,14 +229,7 @@ private fun VanishEventCard(
|
||||
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
|
||||
item.relays.forEach { relayUrl ->
|
||||
RelayComplianceRow(
|
||||
relayUrl = relayUrl,
|
||||
vanishDate = item.event.createdAt,
|
||||
status = complianceResults["$relayUrl:${item.event.createdAt}"] ?: ComplianceStatus.UNTESTED,
|
||||
onTest = { onTestCompliance(relayUrl, item.event.createdAt) },
|
||||
)
|
||||
}
|
||||
RenderRelaysWithComplianceResults(item, onTestCompliance)
|
||||
}
|
||||
|
||||
if (item.event.content.isNotBlank()) {
|
||||
@@ -256,10 +248,24 @@ private fun VanishEventCard(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RenderRelaysWithComplianceResults(
|
||||
item: VanishEventItem,
|
||||
onTestCompliance: (NormalizedRelayUrl) -> Unit,
|
||||
) {
|
||||
val complianceResults by item.complianceResults.collectAsStateWithLifecycle()
|
||||
item.relays.forEach { relay ->
|
||||
RelayComplianceRow(
|
||||
relay = relay,
|
||||
status = complianceResults[relay] ?: ComplianceStatus.UNTESTED,
|
||||
onTest = { onTestCompliance(relay) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RelayComplianceRow(
|
||||
relayUrl: String,
|
||||
vanishDate: Long,
|
||||
relay: NormalizedRelayUrl,
|
||||
status: ComplianceStatus,
|
||||
onTest: () -> Unit,
|
||||
) {
|
||||
@@ -271,11 +277,7 @@ private fun RelayComplianceRow(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text =
|
||||
relayUrl
|
||||
.removePrefix("wss://")
|
||||
.removePrefix("ws://")
|
||||
.removeSuffix("/"),
|
||||
text = relay.displayUrl(),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier.weight(1f),
|
||||
maxLines = 1,
|
||||
|
||||
+29
-23
@@ -27,18 +27,23 @@ import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.accessories.downloadFirstEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.accessories.query
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip62RequestToVanish.RequestToVanishEvent
|
||||
import com.vitorpamplona.quartz.nip62RequestToVanish.tags.RelayTag
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Stable
|
||||
data class VanishEventItem(
|
||||
val event: RequestToVanishEvent,
|
||||
val relays: List<String>,
|
||||
val relays: List<NormalizedRelayUrl>,
|
||||
val isAllRelays: Boolean,
|
||||
val complianceResults: MutableStateFlow<Map<NormalizedRelayUrl, ComplianceStatus>> =
|
||||
MutableStateFlow(
|
||||
relays.associateWith { ComplianceStatus.UNTESTED },
|
||||
),
|
||||
)
|
||||
|
||||
enum class ComplianceStatus {
|
||||
@@ -58,14 +63,10 @@ class VanishEventsViewModel : ViewModel() {
|
||||
private val _isLoading = MutableStateFlow(false)
|
||||
val isLoading = _isLoading.asStateFlow()
|
||||
|
||||
private val _complianceResults = MutableStateFlow<Map<String, ComplianceStatus>>(emptyMap())
|
||||
val complianceResults = _complianceResults.asStateFlow()
|
||||
|
||||
fun load() {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
_isLoading.value = true
|
||||
_vanishEvents.value = emptyList()
|
||||
_complianceResults.value = emptyMap()
|
||||
|
||||
val connectedRelays = account.client.connectedRelaysFlow().value
|
||||
if (connectedRelays.isEmpty()) {
|
||||
@@ -86,7 +87,7 @@ class VanishEventsViewModel : ViewModel() {
|
||||
account.client.query(filters = filtersPerRelay).mapNotNull { event ->
|
||||
if (event is RequestToVanishEvent) {
|
||||
val relayTags = event.vanishFromRelays()
|
||||
val isAll = relayTags.contains(RelayTag.EVERYWHERE)
|
||||
val isAll = event.vanishFromAllRelays()
|
||||
VanishEventItem(
|
||||
event = event,
|
||||
relays = relayTags,
|
||||
@@ -102,35 +103,40 @@ class VanishEventsViewModel : ViewModel() {
|
||||
}
|
||||
|
||||
fun testCompliance(
|
||||
relayUrl: String,
|
||||
vanishDate: Long,
|
||||
item: VanishEventItem,
|
||||
relay: NormalizedRelayUrl,
|
||||
) {
|
||||
val key = "$relayUrl:$vanishDate"
|
||||
_complianceResults.value = _complianceResults.value + (key to ComplianceStatus.TESTING)
|
||||
item.complianceResults.update {
|
||||
it + (relay to ComplianceStatus.TESTING)
|
||||
}
|
||||
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val foundEvent =
|
||||
account.client.downloadFirstEvent(
|
||||
relay = relayUrl,
|
||||
relay = relay,
|
||||
filter =
|
||||
Filter(
|
||||
authors = listOf(account.pubKey),
|
||||
until = vanishDate - 1,
|
||||
authors = listOf(item.event.pubKey),
|
||||
until = item.event.createdAt - 1,
|
||||
limit = 1,
|
||||
),
|
||||
)
|
||||
|
||||
_complianceResults.value += (
|
||||
key to
|
||||
if (foundEvent != null) {
|
||||
ComplianceStatus.NON_COMPLIANT
|
||||
} else {
|
||||
ComplianceStatus.COMPLIANT
|
||||
}
|
||||
)
|
||||
item.complianceResults.update {
|
||||
it + (
|
||||
relay to
|
||||
if (foundEvent != null) {
|
||||
ComplianceStatus.NON_COMPLIANT
|
||||
} else {
|
||||
ComplianceStatus.COMPLIANT
|
||||
}
|
||||
)
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
_complianceResults.value += (key to ComplianceStatus.ERROR)
|
||||
item.complianceResults.update {
|
||||
it + (relay to ComplianceStatus.ERROR)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -23,14 +23,16 @@ package com.vitorpamplona.quartz.nip01Core.store.sqlite
|
||||
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.normalizeRelayUrl
|
||||
import com.vitorpamplona.quartz.nip01Core.store.IEventStore
|
||||
|
||||
class EventStore(
|
||||
dbName: String? = "events.db",
|
||||
relayUrl: String? = "wss://quartz.local",
|
||||
relay: NormalizedRelayUrl? = "wss://quartz.local/".normalizeRelayUrl(),
|
||||
val indexStrategy: IndexingStrategy = DefaultIndexingStrategy(),
|
||||
) : IEventStore {
|
||||
val store = SQLiteEventStore(BundledSQLiteDriver(), dbName, relayUrl, indexStrategy)
|
||||
val store = SQLiteEventStore(BundledSQLiteDriver(), dbName, relay, indexStrategy)
|
||||
|
||||
override fun insert(event: Event) = store.insertEvent(event)
|
||||
|
||||
|
||||
+3
-2
@@ -22,6 +22,7 @@ package com.vitorpamplona.quartz.nip01Core.store.sqlite
|
||||
|
||||
import androidx.sqlite.SQLiteConnection
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip62RequestToVanish.RequestToVanishEvent
|
||||
|
||||
class RightToVanishModule(
|
||||
@@ -100,11 +101,11 @@ class RightToVanishModule(
|
||||
|
||||
fun insert(
|
||||
event: Event,
|
||||
relayUrl: String?,
|
||||
relay: NormalizedRelayUrl?,
|
||||
headerId: Long,
|
||||
db: SQLiteConnection,
|
||||
) {
|
||||
if (event is RequestToVanishEvent && event.shouldVanishFrom(relayUrl)) {
|
||||
if (event is RequestToVanishEvent && event.shouldVanishFrom(relay)) {
|
||||
db.prepare(insertRTV).use { stmt ->
|
||||
stmt.bindLong(1, headerId)
|
||||
stmt.bindLong(2, hasher(db).hash(event.pubKey))
|
||||
|
||||
+3
-2
@@ -30,6 +30,7 @@ import com.vitorpamplona.quartz.nip01Core.core.Kind
|
||||
import com.vitorpamplona.quartz.nip01Core.core.OptimizedJsonMapper
|
||||
import com.vitorpamplona.quartz.nip01Core.core.isEphemeral
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip01Core.store.IEventStore
|
||||
import com.vitorpamplona.quartz.nip40Expiration.isExpired
|
||||
import com.vitorpamplona.quartz.utils.EventFactory
|
||||
@@ -40,7 +41,7 @@ import kotlinx.coroutines.withContext
|
||||
class SQLiteEventStore(
|
||||
val driver: SQLiteDriver = BundledSQLiteDriver(),
|
||||
val dbName: String? = "events.db",
|
||||
val relayUrl: String? = null,
|
||||
val relay: NormalizedRelayUrl? = null,
|
||||
val indexStrategy: IndexingStrategy = DefaultIndexingStrategy(),
|
||||
) {
|
||||
companion object {
|
||||
@@ -182,7 +183,7 @@ class SQLiteEventStore(
|
||||
deletionModule.insert(event, db)
|
||||
expirationModule.insert(event, headerId, db)
|
||||
fullTextSearchModule.insert(event, headerId, db)
|
||||
rightToVanishModule.insert(event, relayUrl, headerId, db)
|
||||
rightToVanishModule.insert(event, relay, headerId, db)
|
||||
}
|
||||
|
||||
fun insertEvent(event: Event) {
|
||||
|
||||
+4
-1
@@ -28,6 +28,7 @@ import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
|
||||
import com.vitorpamplona.quartz.nip31Alts.alt
|
||||
import com.vitorpamplona.quartz.nip62RequestToVanish.tags.shouldVanishFrom
|
||||
import com.vitorpamplona.quartz.nip62RequestToVanish.tags.vanishFrom
|
||||
import com.vitorpamplona.quartz.nip62RequestToVanish.tags.vanishFromAllRelays
|
||||
import com.vitorpamplona.quartz.nip62RequestToVanish.tags.vanishFromEverywhere
|
||||
import com.vitorpamplona.quartz.nip62RequestToVanish.tags.vanishFromRelays
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
@@ -42,7 +43,9 @@ class RequestToVanishEvent(
|
||||
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
|
||||
fun vanishFromRelays() = tags.vanishFromRelays()
|
||||
|
||||
fun shouldVanishFrom(relayUrl: String?) = tags.shouldVanishFrom(relayUrl)
|
||||
fun vanishFromAllRelays() = tags.vanishFromAllRelays()
|
||||
|
||||
fun shouldVanishFrom(relay: NormalizedRelayUrl?) = tags.shouldVanishFrom(relay)
|
||||
|
||||
companion object {
|
||||
const val KIND = 62
|
||||
|
||||
+13
-4
@@ -22,6 +22,8 @@ package com.vitorpamplona.quartz.nip62RequestToVanish.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.has
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.isLocalHost
|
||||
import com.vitorpamplona.quartz.utils.ensure
|
||||
|
||||
class RelayTag {
|
||||
@@ -33,16 +35,23 @@ class RelayTag {
|
||||
|
||||
fun notMatch(tag: Array<String>) = !(tag.has(0) && tag[0] == TAG_NAME)
|
||||
|
||||
fun shouldVanishFromEverywhere(tag: Array<String>) = tag.has(1) && tag[0] == TAG_NAME && tag[1] == EVERYWHERE
|
||||
|
||||
fun shouldVanishFrom(
|
||||
tag: Array<String>,
|
||||
relayUrl: String?,
|
||||
) = tag.has(1) && tag[0] == TAG_NAME && (tag[1] == relayUrl || tag[1] == EVERYWHERE)
|
||||
relay: NormalizedRelayUrl,
|
||||
) = tag.has(1) && tag[0] == TAG_NAME && (tag[1] == relay.url || tag[1] == EVERYWHERE)
|
||||
|
||||
fun parse(tag: Array<String>): String? {
|
||||
fun parse(tag: Array<String>): NormalizedRelayUrl? {
|
||||
ensure(tag.has(1)) { return null }
|
||||
ensure(tag[0] == TAG_NAME) { return null }
|
||||
ensure(tag[1].isNotEmpty()) { return null }
|
||||
return tag[1]
|
||||
|
||||
val relay = RelayUrlNormalizer.normalizeOrNull(tag[1])
|
||||
|
||||
ensure(relay != null && !relay.isLocalHost()) { return null }
|
||||
|
||||
return relay
|
||||
}
|
||||
|
||||
fun assemble(relay: NormalizedRelayUrl) = arrayOf(TAG_NAME, relay.url)
|
||||
|
||||
+9
-1
@@ -22,7 +22,15 @@ package com.vitorpamplona.quartz.nip62RequestToVanish.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArray
|
||||
import com.vitorpamplona.quartz.nip01Core.core.any
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
|
||||
fun TagArray.vanishFromRelays() = mapNotNull(RelayTag::parse)
|
||||
|
||||
fun TagArray.shouldVanishFrom(relayUrl: String?) = any(RelayTag::shouldVanishFrom, relayUrl)
|
||||
fun TagArray.vanishFromAllRelays() = any(RelayTag::shouldVanishFromEverywhere)
|
||||
|
||||
fun TagArray.shouldVanishFrom(relay: NormalizedRelayUrl?): Boolean =
|
||||
if (relay != null) {
|
||||
any(RelayTag::shouldVanishFrom, relay)
|
||||
} else {
|
||||
any(RelayTag::shouldVanishFromEverywhere)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user