Avoids allocating Strings for the JSON conversion when checking if a filter has changed.
This commit is contained in:
@@ -221,7 +221,7 @@ abstract class NostrDataSource(val debugName: String) {
|
|||||||
// saves the channels that are currently active
|
// saves the channels that are currently active
|
||||||
val activeSubscriptions = subscriptions.values.filter { it.typedFilters != null }
|
val activeSubscriptions = subscriptions.values.filter { it.typedFilters != null }
|
||||||
// saves the current content to only update if it changes
|
// 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)
|
changingFilters.getAndSet(true)
|
||||||
|
|
||||||
@@ -245,7 +245,7 @@ abstract class NostrDataSource(val debugName: String) {
|
|||||||
Client.close(updatedSubscription.id)
|
Client.close(updatedSubscription.id)
|
||||||
} else {
|
} else {
|
||||||
// was active and is still active, check if it has changed.
|
// 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)
|
Client.close(updatedSubscription.id)
|
||||||
if (active) {
|
if (active) {
|
||||||
Client.sendFilter(updatedSubscription.id, updatedSubscriptionNewFilters)
|
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
|
// was not active and is still not active, does nothing
|
||||||
} else {
|
} else {
|
||||||
// was not active and becomes active, sends the filter.
|
// was not active and becomes active, sends the filter.
|
||||||
if (updatedSubscription.toJson() != currentFilters[updatedSubscription.id]) {
|
if (updatedSubscription.hasChangedFiltersFrom(currentFilters[updatedSubscription.id])) {
|
||||||
if (active) {
|
if (active) {
|
||||||
Log.d(
|
Log.d(
|
||||||
this@NostrDataSource.javaClass.simpleName,
|
this@NostrDataSource.javaClass.simpleName,
|
||||||
|
|||||||
@@ -20,8 +20,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.amethyst.service.relays
|
package com.vitorpamplona.amethyst.service.relays
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode
|
|
||||||
import com.vitorpamplona.quartz.events.Event
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
data class Subscription(
|
data class Subscription(
|
||||||
@@ -37,23 +35,36 @@ data class Subscription(
|
|||||||
onEOSE?.let { it(time, relay) }
|
onEOSE?.let { it(time, relay) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toJson(): String {
|
fun hasChangedFiltersFrom(otherFilters: List<TypedFilter>?): Boolean {
|
||||||
return Event.mapper.writeValueAsString(toJsonObject())
|
if (typedFilters == null && otherFilters == null) return false
|
||||||
}
|
if (typedFilters?.size != otherFilters?.size) return true
|
||||||
|
|
||||||
fun toJsonObject(): JsonNode {
|
typedFilters?.forEachIndexed { index, typedFilter ->
|
||||||
val factory = Event.mapper.nodeFactory
|
val otherFilter = otherFilters?.getOrNull(index) ?: return true
|
||||||
|
|
||||||
return factory.objectNode().apply {
|
// Does not check SINCE on purpose. Avoids replacing the filter if SINCE was all that changed.
|
||||||
put("id", id)
|
// fast check
|
||||||
typedFilters?.also { filters ->
|
if (typedFilter.filter.authors?.size != otherFilter.filter.authors?.size ||
|
||||||
replace(
|
typedFilter.filter.ids?.size != otherFilter.filter.ids?.size ||
|
||||||
"typedFilters",
|
typedFilter.filter.tags?.size != otherFilter.filter.tags?.size ||
|
||||||
factory.arrayNode(filters.size).apply {
|
typedFilter.filter.kinds?.size != otherFilter.filter.kinds?.size ||
|
||||||
filters.forEach { filter -> add(filter.toJsonObject()) }
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,73 +20,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.amethyst.service.relays
|
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(
|
class TypedFilter(
|
||||||
val types: Set<FeedType>,
|
val types: Set<FeedType>,
|
||||||
val filter: JsonFilter,
|
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) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -58,7 +58,6 @@ import com.vitorpamplona.amethyst.ui.note.NoteCompose
|
|||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
|
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
|
||||||
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
|
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
|
||||||
import kotlin.time.ExperimentalTime
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RefresheableFeedView(
|
fun RefresheableFeedView(
|
||||||
@@ -205,7 +204,7 @@ private fun WatchScrollToTop(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class, ExperimentalTime::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun FeedLoaded(
|
private fun FeedLoaded(
|
||||||
state: FeedState.Loaded,
|
state: FeedState.Loaded,
|
||||||
|
|||||||
Reference in New Issue
Block a user