From 21133c34c03b271a3eaafabd728561ab4142ca11 Mon Sep 17 00:00:00 2001 From: M Date: Tue, 24 Mar 2026 07:55:30 +1100 Subject: [PATCH] Fix search: use resolveDetailed() for proper timeout handling The search bar was using resolve() which lets NamecoinLookupException.ServersUnreachable propagate as an exception, causing 'servers unreachable' to appear immediately instead of waiting for the actual lookup to complete. Switch to resolveDetailed() which catches exceptions internally and returns typed outcomes (Success/NameNotFound/NoNostrField/ ServersUnreachable/InvalidIdentifier/Timeout). The search screen now shows Loading for the full duration of the attempt (up to 20s) and only shows the error after all servers have actually been tried. --- .../amethyst/desktop/ui/SearchScreen.kt | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/SearchScreen.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/SearchScreen.kt index 0a47d4e69..89a369ba6 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/SearchScreen.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/SearchScreen.kt @@ -173,22 +173,29 @@ fun SearchScreen( var namecoinState by remember { mutableStateOf(null) } - // Resolve Namecoin identifiers with cancellation of stale lookups + // Resolve Namecoin identifiers with cancellation of stale lookups. + // Uses resolveDetailed() which returns typed outcomes instead of throwing, + // so we get proper NotFound/Expired/ServersUnreachable states. LaunchedEffect(displayText, namecoinEnabled) { if (!namecoinEnabled || !isNamecoinQuery || namecoinService == null) { namecoinState = null return@LaunchedEffect } namecoinState = NamecoinResolveState.Loading - try { - val result = namecoinService.resolve(displayText.trim()) - namecoinState = if (result != null) { - NamecoinResolveState.Resolved(result) - } else { + val outcome = namecoinService.resolveDetailed(displayText.trim()) + namecoinState = when (outcome) { + is NamecoinResolveOutcome.Success -> + NamecoinResolveState.Resolved(outcome.result) + is NamecoinResolveOutcome.NameNotFound -> NamecoinResolveState.NotFound - } - } catch (e: Exception) { - namecoinState = NamecoinResolveState.Error(e.message ?: "Resolution failed") + is NamecoinResolveOutcome.NoNostrField -> + NamecoinResolveState.Error("Name exists but has no Nostr pubkey") + is NamecoinResolveOutcome.ServersUnreachable -> + NamecoinResolveState.Error("ElectrumX servers unreachable — check your connection or try again") + is NamecoinResolveOutcome.InvalidIdentifier -> + NamecoinResolveState.Error("Invalid Namecoin identifier") + is NamecoinResolveOutcome.Timeout -> + NamecoinResolveState.Error("Resolution timed out — servers may be slow, try again") } }