mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-11 16:57:39 +00:00
fix: exclude client tag from note text search matching
Searching for an app name (e.g. "Amethyst") was returning every event published through that client, because the local-cache note search matched the search term against all tag values including the NIP-89 ["client", ...] tag. Skip the client tag when matching tag values in findNotesStartingWith. https://claude.ai/code/session_01YMs6aXuvs5NaYjzyPH6Zqj
This commit is contained in:
@@ -252,6 +252,7 @@ import com.vitorpamplona.quartz.nip87Ecash.fedimint.FedimintEvent
|
||||
import com.vitorpamplona.quartz.nip87Ecash.recommendation.MintRecommendationEvent
|
||||
import com.vitorpamplona.quartz.nip88Polls.poll.PollEvent
|
||||
import com.vitorpamplona.quartz.nip88Polls.response.PollResponseEvent
|
||||
import com.vitorpamplona.quartz.nip89AppHandlers.clientTag.ClientTag
|
||||
import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent
|
||||
import com.vitorpamplona.quartz.nip89AppHandlers.recommendation.AppRecommendationEvent
|
||||
import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryRequest.NIP90ContentDiscoveryRequestEvent
|
||||
@@ -2380,6 +2381,13 @@ object LocalCache : ILocalCache, ICacheProvider {
|
||||
note.event is AppSpecificDataEvent
|
||||
)
|
||||
|
||||
/**
|
||||
* Tag names whose values should not match text searches. The `client` tag
|
||||
* names the app that published the event, so searching for "Amethyst"
|
||||
* would otherwise return every event posted through Amethyst.
|
||||
*/
|
||||
private val excludedTagNamesFromSearch = setOf(ClientTag.TAG_NAME)
|
||||
|
||||
fun findNotesStartingWith(
|
||||
text: String,
|
||||
hiddenUsers: HiddenUsersState,
|
||||
@@ -2419,7 +2427,7 @@ object LocalCache : ILocalCache, ICacheProvider {
|
||||
return@filter false
|
||||
}
|
||||
|
||||
if (note.event?.tags?.tagValueContains(text, true) == true ||
|
||||
if (note.event?.tags?.tagValueContains(text, true, excludedTagNamesFromSearch) == true ||
|
||||
note.idHex.startsWith(text, true)
|
||||
) {
|
||||
return@filter !note.isHiddenFor(hiddenUsers.flow.value)
|
||||
@@ -2440,7 +2448,7 @@ object LocalCache : ILocalCache, ICacheProvider {
|
||||
return@filter false
|
||||
}
|
||||
|
||||
if (addressable.event?.tags?.tagValueContains(text, true) == true ||
|
||||
if (addressable.event?.tags?.tagValueContains(text, true, excludedTagNamesFromSearch) == true ||
|
||||
addressable.idHex.startsWith(text, true)
|
||||
) {
|
||||
return@filter !addressable.isHiddenFor(hiddenUsers.flow.value)
|
||||
|
||||
@@ -171,12 +171,14 @@ fun TagArray.isAnyLowercaseTagged(
|
||||
) = this.fastAny { it.size > 1 && it[0] == tagName && it[1].lowercase() in tagValues }
|
||||
|
||||
/**
|
||||
* Returns `true` if at least one tag has value that contains [text]
|
||||
* Returns `true` if at least one tag has value that contains [text].
|
||||
* Tags whose name is in [exceptNames] are skipped.
|
||||
*/
|
||||
fun TagArray.tagValueContains(
|
||||
text: String,
|
||||
ignoreCase: Boolean = false,
|
||||
) = this.fastAny { it.size > 1 && it[1].contains(text, ignoreCase) }
|
||||
exceptNames: Set<String> = emptySet(),
|
||||
) = this.fastAny { it.size > 1 && it[0] !in exceptNames && it[1].contains(text, ignoreCase) }
|
||||
|
||||
fun TagArray.containsAllTagNamesWithValues(names: Set<String>): Boolean {
|
||||
val remaining = names.toMutableSet()
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip01Core.core
|
||||
|
||||
import com.vitorpamplona.quartz.nip89AppHandlers.clientTag.ClientTag
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class TagArrayTest {
|
||||
private val tags: TagArray =
|
||||
arrayOf(
|
||||
arrayOf("d", "30022"),
|
||||
arrayOf("a", "31990:460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c:1685802317447", "wss://nostr.wine/", "android"),
|
||||
arrayOf("alt", "App recommendations by the author"),
|
||||
arrayOf("client", "Amethyst"),
|
||||
)
|
||||
|
||||
@Test
|
||||
fun matchesTagValues() {
|
||||
assertTrue(tags.tagValueContains("recommendations", ignoreCase = true))
|
||||
assertTrue(tags.tagValueContains("amethyst", ignoreCase = true))
|
||||
assertFalse(tags.tagValueContains("missing", ignoreCase = true))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun skipsExcludedTagNames() {
|
||||
assertFalse(tags.tagValueContains("amethyst", ignoreCase = true, exceptNames = setOf(ClientTag.TAG_NAME)))
|
||||
// other tags still match when the client tag is excluded
|
||||
assertTrue(tags.tagValueContains("recommendations", ignoreCase = true, exceptNames = setOf(ClientTag.TAG_NAME)))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user