pasting an npub1... / nprofile1... / nevent1... / naddr1... / note1... in the search bar will navigate to the target instead of just showing search results.

This commit is contained in:
davotoula
2026-04-23 14:57:52 +02:00
parent b84d286ba9
commit 8c41d7ea90
2 changed files with 41 additions and 10 deletions
@@ -40,6 +40,7 @@ import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.relayClient.searchCommand.SearchQueryState
import com.vitorpamplona.amethyst.ui.dal.DefaultFeedOrder
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor
import com.vitorpamplona.amethyst.ui.note.creators.userSuggestions.userUriPrefixes
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
@@ -58,6 +59,7 @@ import com.vitorpamplona.quartz.nip19Bech32.entities.NSec
import com.vitorpamplona.quartz.utils.Hex
import com.vitorpamplona.quartz.utils.Rfc3986
import com.vitorpamplona.quartz.utils.startsWithAny
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.Flow
@@ -171,19 +173,47 @@ class SearchBarViewModel(
}.flowOn(Dispatchers.IO)
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
val directEventResolver: Flow<Route?> =
val directRouteResolver: Flow<Route?> =
searchTerm
.debounce(200)
.mapLatest { term ->
if (term.isBlank()) return@mapLatest null
runCatching {
when (val parsed = Nip19Parser.uriToRoute(term)?.entity) {
is NEvent -> Route.Note(parsed.hex)
is NNote -> Route.Note(parsed.hex)
is NAddress -> Route.Note(parsed.aTag())
else -> null
val parsed =
runCatching { Nip19Parser.uriToRoute(term)?.entity }
.onFailure { if (it is CancellationException) throw it }
.getOrNull()
?: return@mapLatest null
when (parsed) {
is NPub -> {
LocalCache.consume(parsed)
Route.Profile(parsed.hex)
}
is NProfile -> {
LocalCache.consume(parsed)
Route.Profile(parsed.hex)
}
is NNote -> {
LocalCache.consume(parsed)
Route.Note(parsed.hex)
}
is NEvent -> {
LocalCache.consume(parsed)
routeFor(LocalCache.getOrCreateNote(parsed.hex), account)
?: Route.EventRedirect(parsed.hex)
}
is NAddress -> {
LocalCache.consume(parsed)
routeFor(LocalCache.getOrCreateAddressableNote(parsed.address()), account)
?: Route.EventRedirect(parsed.aTag())
}
else -> {
null
}
}
}.getOrNull()
}.flowOn(Dispatchers.IO)
val searchResultsUsers =
@@ -186,7 +186,7 @@ private fun SearchBar(
// bech32 auto-resolve: navigate on hit without displaying results
launch {
searchBarViewModel.directEventResolver.filterNotNull().collect { route ->
searchBarViewModel.directRouteResolver.filterNotNull().collect { route ->
nav.nav(route)
searchBarViewModel.clear()
}
@@ -215,6 +215,7 @@ private fun hasNonDefaultFilters(
}
@OptIn(ExperimentalMaterial3Api::class)
@Suppress("AssignedValueIsNeverRead")
@Composable
private fun SearchFilterRow(searchBarViewModel: SearchBarViewModel) {
val currentScope by searchBarViewModel.scope.collectAsStateWithLifecycle()