Avoids allocating Strings for the JSON conversion when checking if a filter has changed.

pull/800/head
Vitor Pamplona 2024-03-05 16:09:50 -05:00
rodzic 3b3ca06c1c
commit f6ffb87e20
4 zmienionych plików z 32 dodań i 88 usunięć

Wyświetl plik

@ -221,7 +221,7 @@ abstract class NostrDataSource(val debugName: String) {
// saves the channels that are currently active
val activeSubscriptions = subscriptions.values.filter { it.typedFilters != null }
// saves the current content to only update if it changes
val currentFilters = activeSubscriptions.associate { it.id to it.toJson() }
val currentFilters = activeSubscriptions.associate { it.id to it.typedFilters }
changingFilters.getAndSet(true)
@ -245,7 +245,7 @@ abstract class NostrDataSource(val debugName: String) {
Client.close(updatedSubscription.id)
} else {
// was active and is still active, check if it has changed.
if (updatedSubscription.toJson() != currentFilters[updatedSubscription.id]) {
if (updatedSubscription.hasChangedFiltersFrom(currentFilters[updatedSubscription.id])) {
Client.close(updatedSubscription.id)
if (active) {
Client.sendFilter(updatedSubscription.id, updatedSubscriptionNewFilters)
@ -265,7 +265,7 @@ abstract class NostrDataSource(val debugName: String) {
// was not active and is still not active, does nothing
} else {
// was not active and becomes active, sends the filter.
if (updatedSubscription.toJson() != currentFilters[updatedSubscription.id]) {
if (updatedSubscription.hasChangedFiltersFrom(currentFilters[updatedSubscription.id])) {
if (active) {
Log.d(
this@NostrDataSource.javaClass.simpleName,

Wyświetl plik

@ -20,8 +20,6 @@
*/
package com.vitorpamplona.amethyst.service.relays
import com.fasterxml.jackson.databind.JsonNode
import com.vitorpamplona.quartz.events.Event
import java.util.UUID
data class Subscription(
@ -37,23 +35,36 @@ data class Subscription(
onEOSE?.let { it(time, relay) }
}
fun toJson(): String {
return Event.mapper.writeValueAsString(toJsonObject())
}
fun hasChangedFiltersFrom(otherFilters: List<TypedFilter>?): Boolean {
if (typedFilters == null && otherFilters == null) return false
if (typedFilters?.size != otherFilters?.size) return true
fun toJsonObject(): JsonNode {
val factory = Event.mapper.nodeFactory
typedFilters?.forEachIndexed { index, typedFilter ->
val otherFilter = otherFilters?.getOrNull(index) ?: return true
return factory.objectNode().apply {
put("id", id)
typedFilters?.also { filters ->
replace(
"typedFilters",
factory.arrayNode(filters.size).apply {
filters.forEach { filter -> add(filter.toJsonObject()) }
},
)
// Does not check SINCE on purpose. Avoids replacing the filter if SINCE was all that changed.
// fast check
if (typedFilter.filter.authors?.size != otherFilter.filter.authors?.size ||
typedFilter.filter.ids?.size != otherFilter.filter.ids?.size ||
typedFilter.filter.tags?.size != otherFilter.filter.tags?.size ||
typedFilter.filter.kinds?.size != otherFilter.filter.kinds?.size ||
typedFilter.filter.limit != otherFilter.filter.limit ||
typedFilter.filter.search?.length != otherFilter.filter.search?.length ||
typedFilter.filter.until != otherFilter.filter.until
) {
return true
}
// deep check
if (typedFilter.filter.ids != otherFilter.filter.ids ||
typedFilter.filter.authors != otherFilter.filter.authors ||
typedFilter.filter.tags != otherFilter.filter.tags ||
typedFilter.filter.kinds != otherFilter.filter.kinds ||
typedFilter.filter.search != otherFilter.filter.search
) {
return true
}
}
return false
}
}

Wyświetl plik

@ -20,73 +20,7 @@
*/
package com.vitorpamplona.amethyst.service.relays
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.node.ArrayNode
import com.vitorpamplona.quartz.events.Event
class TypedFilter(
val types: Set<FeedType>,
val filter: JsonFilter,
) {
fun toJson(): String {
return Event.mapper.writeValueAsString(toJsonObject())
}
fun toJsonObject(): JsonNode {
val factory = Event.mapper.nodeFactory
return factory.objectNode().apply {
replace("types", typesToJson(types))
replace("filter", filterToJson(filter))
}
}
fun typesToJson(types: Set<FeedType>): ArrayNode {
val factory = Event.mapper.nodeFactory
return factory.arrayNode(types.size).apply { types.forEach { add(it.name.lowercase()) } }
}
fun filterToJson(filter: JsonFilter): JsonNode {
val factory = Event.mapper.nodeFactory
return factory.objectNode().apply {
filter.ids?.run {
replace(
"ids",
factory.arrayNode(filter.ids.size).apply { filter.ids.forEach { add(it) } },
)
}
filter.authors?.run {
replace(
"authors",
factory.arrayNode(filter.authors.size).apply { filter.authors.forEach { add(it) } },
)
}
filter.kinds?.run {
replace(
"kinds",
factory.arrayNode(filter.kinds.size).apply { filter.kinds.forEach { add(it) } },
)
}
filter.tags?.run {
entries.forEach { kv ->
replace(
"#${kv.key}",
factory.arrayNode(kv.value.size).apply { kv.value.forEach { add(it) } },
)
}
}
/*
Does not include since in the json comparison
filter.since?.run {
val jsonObjectSince = JsonObject()
entries.forEach { sincePairs ->
jsonObjectSince.addProperty(sincePairs.key, "${sincePairs.value}")
}
jsonObject.add("since", jsonObjectSince)
}*/
filter.until?.run { put("until", filter.until) }
filter.limit?.run { put("limit", filter.limit) }
filter.search?.run { put("search", filter.search) }
}
}
}
)

Wyświetl plik

@ -58,7 +58,6 @@ import com.vitorpamplona.amethyst.ui.note.NoteCompose
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
import kotlin.time.ExperimentalTime
@Composable
fun RefresheableFeedView(
@ -205,7 +204,7 @@ private fun WatchScrollToTop(
}
}
@OptIn(ExperimentalFoundationApi::class, ExperimentalTime::class)
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun FeedLoaded(
state: FeedState.Loaded,