mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-12 09:13:23 +00:00
feat(search): inline Namecoin resolution indicator in global search bar
Reuses the NamecoinResolutionRow composable already shipping for the onchain-zap recipient field, promoting it from ui/screen/loggedIn/wallet/ to a generic ui/components/namecoin/ location so it can be mounted anywhere a .bit-shaped search input may race the local-cache prefix search. In the global search bar, typing a bare ".bit" host (e.g. "testls.bit") used to surface a cached sibling profile like "m@testls.bit" first (LocalCache.findUsersStartingWith hits the prefix) and only several seconds later be corrected by the slower on-chain ElectrumX resolution from SearchBarViewModel.directNip05Resolver. No in-flight indicator and no feedback on hard failures (timeout, malformed record, etc.). Changes: - git-rename NamecoinResolutionRow.kt and its test from ui/screen/loggedIn/wallet/ to ui/components/namecoin/, updating the package declaration only. - Add an optional `modifier: Modifier = Modifier` parameter to the composable (standard Compose convention) and wrap the spinner / result / error rows in a Column taking the caller-provided modifier. No visual change in OnchainZapSendDialog. - Update OnchainZapSendDialog import to the new package location. - Mount NamecoinResolutionRow in SearchScreen.SearchBar between SearchTextField and SearchFilterRow, with horizontal padding to match the rest of the bar. onUserResolved navigates to the user and clears the field, matching the bech32 auto-resolve path in SearchBarViewModel.directRouteResolver. State is held in the shared commons.NamecoinResolveState (no new state class introduced) and diagnostic wording comes from the existing mapOutcomeToResolveState helper, so every Namecoin surface continues to produce the same message for the same outcome.
This commit is contained in:
+18
-11
@@ -18,7 +18,7 @@
|
||||
* 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.amethyst.ui.screen.loggedIn.wallet
|
||||
package com.vitorpamplona.amethyst.ui.components.namecoin
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
@@ -111,15 +111,16 @@ fun looksLikeNamecoinIdentifier(raw: String): Boolean {
|
||||
}
|
||||
|
||||
/**
|
||||
* Inline Namecoin resolution indicator + result row, sandwiched between
|
||||
* the recipient text field and the local-cache suggestion dropdown in
|
||||
* [OnchainZapSendDialog].
|
||||
* Inline Namecoin resolution indicator + result row. Designed to be
|
||||
* mounted alongside any text input whose local-cache prefix search can
|
||||
* race ahead of an on-chain `.bit` lookup (the onchain-zap recipient
|
||||
* field and the global search bar both have this race).
|
||||
*
|
||||
* Behaviour:
|
||||
* - Renders nothing when [searchInput] is not a `.bit` identifier.
|
||||
* - Shows a small spinner row ("Resolving on Namecoin…") while the
|
||||
* ElectrumX lookup is in flight (after a 300 ms debounce to match
|
||||
* the dropdown's own debounce).
|
||||
* typical input-field debounce intervals).
|
||||
* - On success, shows the resolved user as a tappable row with a
|
||||
* `MaterialSymbols.Link` badge labelled "Namecoin"; tapping calls
|
||||
* [onUserResolved].
|
||||
@@ -132,12 +133,16 @@ fun looksLikeNamecoinIdentifier(raw: String): Boolean {
|
||||
* The composable is intentionally self-contained: it owns its own
|
||||
* [LaunchedEffect] keyed on [searchInput], so it cancels in-flight
|
||||
* lookups whenever the user keeps typing.
|
||||
*
|
||||
* @param modifier applied to the outer `Column` so callers can position
|
||||
* or pad the row (e.g. the search bar pads horizontally).
|
||||
*/
|
||||
@Composable
|
||||
fun NamecoinResolutionRow(
|
||||
searchInput: String,
|
||||
accountViewModel: AccountViewModel,
|
||||
onUserResolved: (User) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val trimmed = remember(searchInput) { searchInput.trim().removePrefix("@") }
|
||||
if (!looksLikeNamecoinIdentifier(trimmed)) return
|
||||
@@ -166,12 +171,14 @@ fun NamecoinResolutionRow(
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(Modifier.size(8.dp))
|
||||
when (val s = state) {
|
||||
null, NamecoinResolveState.Loading -> ResolvingChip(trimmed)
|
||||
is NamecoinResolveState.Resolved -> ResolvedRow(trimmed, s, accountViewModel, onUserResolved)
|
||||
NamecoinResolveState.NotFound -> FailedRow("No record for $trimmed on Namecoin.")
|
||||
is NamecoinResolveState.Error -> FailedRow(s.message)
|
||||
Column(modifier = modifier) {
|
||||
Spacer(Modifier.size(8.dp))
|
||||
when (val s = state) {
|
||||
null, NamecoinResolveState.Loading -> ResolvingChip(trimmed)
|
||||
is NamecoinResolveState.Resolved -> ResolvedRow(trimmed, s, accountViewModel, onUserResolved)
|
||||
NamecoinResolveState.NotFound -> FailedRow("No record for $trimmed on Namecoin.")
|
||||
is NamecoinResolveState.Error -> FailedRow(s.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+19
@@ -79,6 +79,7 @@ import com.vitorpamplona.amethyst.commons.search.SearchSource
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.nip11RelayInfo.loadRelayInfo
|
||||
import com.vitorpamplona.amethyst.service.relayClient.searchCommand.TextSearchDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.components.namecoin.NamecoinResolutionRow
|
||||
import com.vitorpamplona.amethyst.ui.feeds.WatchLifecycleAndUpdateModel
|
||||
import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold
|
||||
import com.vitorpamplona.amethyst.ui.layouts.rememberFeedContentPadding
|
||||
@@ -194,6 +195,24 @@ private fun SearchBar(
|
||||
|
||||
Column(modifier = Modifier.statusBarsPadding()) {
|
||||
SearchTextField(searchBarViewModel, Modifier)
|
||||
// Inline Namecoin lookup feedback for the global search field.
|
||||
// Mirrors the wiring in OnchainZapSendDialog: the local prefix
|
||||
// search can race ahead of the on-chain resolution and show a
|
||||
// cached sibling profile (e.g. "m@testls.bit") before the bare
|
||||
// ".bit" host resolves to its `_@host` profile. Surfaces the
|
||||
// in-flight state, the eventual on-chain match, and any failure
|
||||
// explicitly. Tapping the resolved row navigates to the user and
|
||||
// clears the search field, matching the existing bech32 auto-
|
||||
// resolve behaviour in `SearchBarViewModel.directRouteResolver`.
|
||||
NamecoinResolutionRow(
|
||||
searchInput = searchBarViewModel.searchValue,
|
||||
accountViewModel = accountViewModel,
|
||||
onUserResolved = { user ->
|
||||
nav.nav(routeFor(user))
|
||||
searchBarViewModel.clear()
|
||||
},
|
||||
modifier = Modifier.padding(horizontal = 10.dp),
|
||||
)
|
||||
SearchFilterRow(searchBarViewModel)
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -69,6 +69,7 @@ import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
|
||||
import com.vitorpamplona.amethyst.commons.onchain.OnchainZapSendResult
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.ui.components.namecoin.NamecoinResolutionRow
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.EmptyNav
|
||||
import com.vitorpamplona.amethyst.ui.note.UserPicture
|
||||
import com.vitorpamplona.amethyst.ui.note.creators.userSuggestions.ShowUserSuggestionList
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
* 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.amethyst.ui.screen.loggedIn.wallet
|
||||
package com.vitorpamplona.amethyst.ui.components.namecoin
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.nip05DnsIdentifiers.namecoin.NamecoinResolveState
|
||||
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.NamecoinNostrResult
|
||||
Reference in New Issue
Block a user