mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-07-22 15:52:22 +00:00
feat: rank conversation participants first in @-mention suggestions
Adds an optional priorityPubkeys supplier to UserSuggestionState that stable-sorts search results so the current conversation's participants appear before network-wide matches (ranking only, never filters). Wired per chat context: - MLS/Marmot groups: live MLS member list - NIP-17 DMs/groups: the room's users - Public chats (NIP-28): authors who have posted in the channel - Nests audio rooms: MeetingSpaceEvent participants + host https://claude.ai/code/session_013NWdjCSegsf2FYSPPANX3n
This commit is contained in:
@@ -28,6 +28,7 @@ import com.vitorpamplona.amethyst.logTime
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.service.relayClient.searchCommand.SearchQueryState
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.normalizeRelayUrlOrNull
|
||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.INip05Client
|
||||
@@ -58,10 +59,20 @@ val userUriPrefixes =
|
||||
DualCase("nostr:nprofile"),
|
||||
)
|
||||
|
||||
/**
|
||||
* Drives the @-mention autocomplete dropdown: searches the local cache,
|
||||
* relays, and NIP-05 identifiers for the word currently being typed.
|
||||
*
|
||||
* [priorityPubkeys] is a live supplier of pubkeys to rank first in the
|
||||
* results — pass the current conversation's participants (NIP-17 room
|
||||
* users, public-chat authors, MLS group members, …) so they beat
|
||||
* network-wide matches. Ranking only; it never filters anyone out.
|
||||
*/
|
||||
@Stable
|
||||
class UserSuggestionState(
|
||||
val account: Account,
|
||||
val nip05Client: INip05Client,
|
||||
val priorityPubkeys: () -> Set<HexKey> = { emptySet() },
|
||||
) {
|
||||
val invalidations = MutableStateFlow(0)
|
||||
val currentWord = MutableStateFlow("")
|
||||
@@ -158,7 +169,13 @@ class UserSuggestionState(
|
||||
}
|
||||
if (prefix != null) {
|
||||
logTime("UserSuggestionState Search $prefix version $version") {
|
||||
account.cache.findUsersStartingWith(prefix, account)
|
||||
val found = account.cache.findUsersStartingWith(prefix, account)
|
||||
val priority = priorityPubkeys()
|
||||
if (priority.isEmpty()) {
|
||||
found
|
||||
} else {
|
||||
found.sortedByDescending { it.pubkeyHex in priority }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
emptyList()
|
||||
|
||||
@@ -190,7 +190,12 @@ fun MarmotGroupMessageComposer(
|
||||
|
||||
val userSuggestions =
|
||||
remember(nostrGroupId) {
|
||||
UserSuggestionState(accountViewModel.account, accountViewModel.nip05ClientBuilder())
|
||||
val group = accountViewModel.account.marmotGroupList.getOrCreateGroup(nostrGroupId)
|
||||
UserSuggestionState(
|
||||
accountViewModel.account,
|
||||
accountViewModel.nip05ClientBuilder(),
|
||||
priorityPubkeys = { group.members.value.mapTo(mutableSetOf()) { it.pubkey } },
|
||||
)
|
||||
}
|
||||
|
||||
DisposableEffect(nostrGroupId) {
|
||||
|
||||
@@ -261,7 +261,12 @@ class ChatNewMessageViewModel :
|
||||
this.canAddZapRaiser = hasLnAddress()
|
||||
|
||||
this.userSuggestions?.reset()
|
||||
this.userSuggestions = UserSuggestionState(accountVM.account, accountVM.nip05ClientBuilder())
|
||||
this.userSuggestions =
|
||||
UserSuggestionState(
|
||||
accountVM.account,
|
||||
accountVM.nip05ClientBuilder(),
|
||||
priorityPubkeys = { room.value?.users ?: emptySet() },
|
||||
)
|
||||
|
||||
this.emojiSuggestions?.reset()
|
||||
this.emojiSuggestions = EmojiSuggestionState(accountVM.account)
|
||||
|
||||
@@ -188,7 +188,14 @@ open class ChannelNewMessageViewModel :
|
||||
this.canAddZapRaiser = hasLnAddress()
|
||||
|
||||
this.userSuggestions?.reset()
|
||||
this.userSuggestions = UserSuggestionState(accountVM.account, accountVM.nip05ClientBuilder())
|
||||
this.userSuggestions =
|
||||
UserSuggestionState(
|
||||
accountVM.account,
|
||||
accountVM.nip05ClientBuilder(),
|
||||
priorityPubkeys = {
|
||||
channel?.participatingAuthors(0)?.mapTo(mutableSetOf()) { it.pubkeyHex } ?: emptySet()
|
||||
},
|
||||
)
|
||||
|
||||
this.emojiSuggestions?.reset()
|
||||
this.emojiSuggestions = EmojiSuggestionState(accountVM.account)
|
||||
|
||||
@@ -194,7 +194,16 @@ open class NestNewMessageViewModel :
|
||||
this.canAddZapRaiser = hasLnAddress()
|
||||
|
||||
this.userSuggestions?.reset()
|
||||
this.userSuggestions = UserSuggestionState(accountVM.account, accountVM.nip05ClientBuilder())
|
||||
this.userSuggestions =
|
||||
UserSuggestionState(
|
||||
accountVM.account,
|
||||
accountVM.nip05ClientBuilder(),
|
||||
priorityPubkeys = {
|
||||
(room?.event as? MeetingSpaceEvent)?.let { space ->
|
||||
space.participantKeys().toMutableSet().apply { add(space.pubKey) }
|
||||
} ?: emptySet()
|
||||
},
|
||||
)
|
||||
|
||||
this.emojiSuggestions?.reset()
|
||||
this.emojiSuggestions = EmojiSuggestionState(accountVM.account)
|
||||
|
||||
Reference in New Issue
Block a user