From 67fd48c5b8e7ef2e7cdfc120a0a127555e119ee0 Mon Sep 17 00:00:00 2001 From: M Date: Thu, 26 Mar 2026 08:43:39 +1100 Subject: [PATCH] refactor: simplify .bit search resolution per review feedback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address @vitorpamplona's review comments on the last commit: 1. Remove NamecoinResolutionState sealed class and status banners — the existing NIP-05 resolver already handles .bit, no need for separate Namecoin-specific UI state (spinner, chain emoji, error) 2. Remove separate toNip05IdOrNull() resolution path — widen the existing `if (term.contains('@'))` gate in both SearchBarViewModel and UserSuggestionState to also accept bare .bit domains (synthesize _@domain.bit → existing NIP-05 flow) 3. Remove d/ and id/ identifier support from search — per feedback, only NIP-05 style resolution (.bit) in search 4. Remove special Namecoin result rendering in ImportFollowList — just show UserLine for all results, no separation needed 5. Keep a lightweight 'Namecoin' label on resolved users in search — namecoinResolvedUser StateFlow tracks the .bit-resolved user, shown as a small label above the UserCompose row Net: -405 lines, +50 lines across 6 files. --- .../userSuggestions/ShowUserSuggestionList.kt | 60 +-------- .../userSuggestions/UserSuggestionState.kt | 89 +++---------- .../ImportFollowListSelectUserScreen.kt | 121 +----------------- .../loggedIn/search/SearchBarViewModel.kt | 62 +++------ .../ui/screen/loggedIn/search/SearchScreen.kt | 121 ++---------------- amethyst/src/main/res/values/strings.xml | 2 +- 6 files changed, 50 insertions(+), 405 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/userSuggestions/ShowUserSuggestionList.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/userSuggestions/ShowUserSuggestionList.kt index 7a0ae4ef3..37c1ad128 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/userSuggestions/ShowUserSuggestionList.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/userSuggestions/ShowUserSuggestionList.kt @@ -30,33 +30,23 @@ import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.LocalTextStyle -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.text.AnnotatedString -import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.vitorpamplona.amethyst.commons.model.nip05DnsIdentifiers.Nip05State import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.service.relayClient.searchCommand.UserSearchDataSourceSubscription import com.vitorpamplona.amethyst.ui.layouts.listItem.SlimListItem import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture -import com.vitorpamplona.amethyst.ui.note.ObserveAndRenderNIP05VerifiedSymbol import com.vitorpamplona.amethyst.ui.note.UsernameDisplay import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel +import com.vitorpamplona.amethyst.ui.screen.loggedIn.qrcode.WatchAndDisplayNip05Row import com.vitorpamplona.amethyst.ui.theme.DividerThickness -import com.vitorpamplona.amethyst.ui.theme.Font14SP -import com.vitorpamplona.amethyst.ui.theme.NIP05IconSize import com.vitorpamplona.amethyst.ui.theme.Size55dp -import com.vitorpamplona.amethyst.ui.theme.nip05 import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -155,51 +145,3 @@ fun UserLine( }, ) } - -@Composable -private fun WatchAndDisplayNip05Row( - user: User, - accountViewModel: AccountViewModel, -) { - val nip05StateMetadata by user.nip05State().flow.collectAsStateWithLifecycle() - - when (val nip05State = nip05StateMetadata) { - is Nip05State.Exists -> { - NonClickableObserveAndDisplayNIP05(nip05State, accountViewModel) - } - - else -> { - Text( - text = user.pubkeyDisplayHex(), - fontSize = Font14SP, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) - } - } -} - -@Composable -private fun NonClickableObserveAndDisplayNIP05( - nip05State: Nip05State.Exists, - accountViewModel: AccountViewModel, -) { - if (nip05State.nip05.name != "_") { - Text( - text = remember(nip05State) { AnnotatedString(nip05State.nip05.name) }, - fontSize = Font14SP, - color = MaterialTheme.colorScheme.nip05, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) - } - - ObserveAndRenderNIP05VerifiedSymbol(nip05State, 1, NIP05IconSize, accountViewModel) - - Text( - text = nip05State.nip05.domain, - style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.nip05, fontSize = Font14SP), - maxLines = 1, - overflow = TextOverflow.Visible, - ) -} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/userSuggestions/UserSuggestionState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/userSuggestions/UserSuggestionState.kt index 9695346a7..4adc385d5 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/userSuggestions/UserSuggestionState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/userSuggestions/UserSuggestionState.kt @@ -31,7 +31,6 @@ import com.vitorpamplona.quartz.nip01Core.core.toHexKey import com.vitorpamplona.quartz.nip01Core.relay.normalizer.normalizeRelayUrlOrNull import com.vitorpamplona.quartz.nip05DnsIdentifiers.INip05Client import com.vitorpamplona.quartz.nip05DnsIdentifiers.Nip05Id -import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.NamecoinNameResolver import com.vitorpamplona.quartz.nip19Bech32.Nip19Parser import com.vitorpamplona.quartz.nip19Bech32.entities.NProfile import com.vitorpamplona.quartz.nip19Bech32.entities.NPub @@ -58,44 +57,6 @@ val userUriPrefixes = DualCase("nostr:nprofile"), ) -/** UI state for Namecoin resolution progress. */ -sealed class NamecoinResolutionState { - data object Idle : NamecoinResolutionState() - - data object Resolving : NamecoinResolutionState() - - data class Resolved( - val user: User, - val namecoinName: String, - ) : NamecoinResolutionState() - - data class Error( - val message: String, - ) : NamecoinResolutionState() -} - -/** Returns a [Nip05Id] for identifiers that should go through NIP-05 / Namecoin resolution. */ -private fun toNip05IdOrNull(prefix: String): Nip05Id? = - when { - prefix.contains('@') -> { - Nip05Id.parse(prefix) - } - - NamecoinNameResolver.isNamecoinIdentifier(prefix) -> { - if (prefix.endsWith(".bit", ignoreCase = true)) { - // Bare .bit domain → synthesize _@domain.bit so Nip05Client routes to Namecoin - Nip05Id("_", prefix.lowercase()) - } else { - // d/ or id/ — wrap as NIP-05 so it reaches the resolver - Nip05Id("_", prefix.lowercase()) - } - } - - else -> { - null - } - } - @Stable class UserSuggestionState( val account: Account, @@ -105,9 +66,6 @@ class UserSuggestionState( val currentWord = MutableStateFlow("") val searchDataSourceState = SearchQueryState(MutableStateFlow(""), account) - /** Tracks Namecoin resolution status for the UI. */ - val namecoinState = MutableStateFlow(NamecoinResolutionState.Idle) - @OptIn(FlowPreview::class) val searchTerm = currentWord @@ -124,38 +82,30 @@ class UserSuggestionState( .map(::userSearchTermOrNull) .map { prefix -> if (prefix != null) { - val nip05 = toNip05IdOrNull(prefix) + // NIP-05 resolution: user@domain or bare .bit domain + val nip05 = + if (prefix.contains('@')) { + Nip05Id.parse(prefix) + } else if (prefix.endsWith(".bit", ignoreCase = true)) { + Nip05Id("_", prefix.lowercase()) + } else { + null + } if (nip05 != null) { - val isNamecoin = NamecoinNameResolver.isNamecoinIdentifier(nip05.toValue()) - if (isNamecoin) namecoinState.emit(NamecoinResolutionState.Resolving) - - val user = - runCatching { - nip05Client.get(nip05)?.let { info -> - val u = account.cache.checkGetOrCreateUser(info.pubkey) - if (u != null) { - info.relays.forEach { - it.normalizeRelayUrlOrNull()?.let { relay -> - account.cache.relayHints.addKey(u.pubkey(), relay) - } + runCatching { + nip05Client.get(nip05)?.let { info -> + val user = account.cache.checkGetOrCreateUser(info.pubkey) + if (user != null) { + info.relays.forEach { + it.normalizeRelayUrlOrNull()?.let { relay -> + account.cache.relayHints.addKey(user.pubkey(), relay) } } - u } - }.getOrNull() - - if (isNamecoin) { - if (user != null) { - namecoinState.emit(NamecoinResolutionState.Resolved(user, prefix)) - } else { - namecoinState.emit(NamecoinResolutionState.Error("Could not resolve $prefix via Namecoin")) + user } - } else { - namecoinState.emit(NamecoinResolutionState.Idle) - } - user + }.getOrNull() } else if (prefix.startsWithAny(userUriPrefixes)) { - namecoinState.emit(NamecoinResolutionState.Idle) runCatching { Nip19Parser.uriToRoute(prefix)?.entity?.let { parsed -> when (parsed) { @@ -182,14 +132,11 @@ class UserSuggestionState( } }.getOrNull() } else if (prefix.length == 64 && Hex.isHex64(prefix)) { - namecoinState.emit(NamecoinResolutionState.Idle) account.cache.getOrCreateUser(prefix) } else { - namecoinState.emit(NamecoinResolutionState.Idle) null } } else { - namecoinState.emit(NamecoinResolutionState.Idle) null } }.flowOn(Dispatchers.IO) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/newUser/ImportFollowListSelectUserScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/newUser/ImportFollowListSelectUserScreen.kt index c53c8b889..9b0c9286c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/newUser/ImportFollowListSelectUserScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/newUser/ImportFollowListSelectUserScreen.kt @@ -20,11 +20,6 @@ */ package com.vitorpamplona.amethyst.ui.screen.loggedIn.newUser -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.expandVertically -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut -import androidx.compose.animation.shrinkVertically import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues @@ -42,7 +37,6 @@ import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.PersonAdd -import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme @@ -73,7 +67,6 @@ import com.vitorpamplona.amethyst.service.relayClient.searchCommand.UserSearchDa import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.routes.Route import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarExtensibleWithBackButton -import com.vitorpamplona.amethyst.ui.note.creators.userSuggestions.NamecoinResolutionState import com.vitorpamplona.amethyst.ui.note.creators.userSuggestions.UserLine import com.vitorpamplona.amethyst.ui.note.creators.userSuggestions.UserSuggestionState import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @@ -107,10 +100,6 @@ class ImportFollowListSelectUserViewModel( userSuggestions.results .stateIn(viewModelScope, SharingStarted.Eagerly, emptyList()) - val namecoinState = - userSuggestions.namecoinState - .stateIn(viewModelScope, SharingStarted.Eagerly, NamecoinResolutionState.Idle) - class Factory( val account: Account, val nip05Client: INip05Client, @@ -177,10 +166,6 @@ private fun InputSelectUserBody( supportingText = { Text(stringRes(R.string.supports_npub_nip_05_hex_and_namecoin_bit_d_id)) }, ) - Spacer(Modifier.height(4.dp)) - - NamecoinStatusBanner(viewModel) - Spacer(Modifier.height(8.dp)) CustomShowUserSuggestionList( @@ -200,88 +185,6 @@ private fun InputSelectUserBody( } } -@Composable -private fun NamecoinStatusBanner(viewModel: ImportFollowListSelectUserViewModel) { - val ncState by viewModel.namecoinState.collectAsStateWithLifecycle() - - AnimatedVisibility( - visible = ncState !is NamecoinResolutionState.Idle, - enter = fadeIn() + expandVertically(), - exit = fadeOut() + shrinkVertically(), - ) { - when (val state = ncState) { - is NamecoinResolutionState.Resolving -> { - Row( - modifier = - Modifier - .fillMaxWidth() - .padding(horizontal = 4.dp, vertical = 6.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - CircularProgressIndicator( - modifier = Modifier.size(16.dp), - strokeWidth = 2.dp, - color = MaterialTheme.colorScheme.primary, - ) - Spacer(Modifier.width(8.dp)) - Text( - "Resolving via Namecoin blockchain\u2026", - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.primary, - ) - } - } - - is NamecoinResolutionState.Resolved -> { - Row( - modifier = - Modifier - .fillMaxWidth() - .padding(horizontal = 4.dp, vertical = 6.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - "\u26D3\uFE0F", - style = MaterialTheme.typography.bodySmall, - ) - Spacer(Modifier.width(6.dp)) - Text( - "Resolved via Namecoin: ${state.namecoinName}", - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.primary, - fontWeight = FontWeight.Medium, - ) - } - } - - is NamecoinResolutionState.Error -> { - Row( - modifier = - Modifier - .fillMaxWidth() - .padding(horizontal = 4.dp, vertical = 6.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - "\u26A0\uFE0F", - style = MaterialTheme.typography.bodySmall, - ) - Spacer(Modifier.width(6.dp)) - Text( - state.message, - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.error, - ) - } - } - - is NamecoinResolutionState.Idle -> { - // nothing - } - } - } -} - @Composable private fun ImportHeader() { Column { @@ -341,7 +244,6 @@ fun CustomWatchResponses( modifier: Modifier = Modifier, ) { val suggestions by viewModel.results.collectAsStateWithLifecycle() - val ncState by viewModel.namecoinState.collectAsStateWithLifecycle() if (suggestions.isNotEmpty()) { LazyColumn( @@ -350,28 +252,7 @@ fun CustomWatchResponses( state = viewModel.listState, ) { itemsIndexed(suggestions, key = { _, item -> item.pubkeyHex }) { _, item -> - val isNamecoinResult = - ncState is NamecoinResolutionState.Resolved && - (ncState as NamecoinResolutionState.Resolved).user == item - - if (isNamecoinResult) { - Column { - Row( - modifier = Modifier.padding(horizontal = 4.dp, vertical = 2.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - "\u26D3\uFE0F Namecoin result", - style = MaterialTheme.typography.labelSmall, - color = MaterialTheme.colorScheme.primary, - fontWeight = FontWeight.Medium, - ) - } - UserLine(item, accountViewModel) { onSelect(item) } - } - } else { - UserLine(item, accountViewModel) { onSelect(item) } - } + UserLine(item, accountViewModel) { onSelect(item) } HorizontalDivider( thickness = DividerThickness, ) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/search/SearchBarViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/search/SearchBarViewModel.kt index 58bcb90c2..0f1e163d5 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/search/SearchBarViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/search/SearchBarViewModel.kt @@ -36,7 +36,6 @@ import com.vitorpamplona.amethyst.model.LocalCache 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.note.creators.userSuggestions.NamecoinResolutionState 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 @@ -44,7 +43,6 @@ import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl import com.vitorpamplona.quartz.nip01Core.relay.normalizer.normalizeRelayUrlOrNull import com.vitorpamplona.quartz.nip05DnsIdentifiers.INip05Client import com.vitorpamplona.quartz.nip05DnsIdentifiers.Nip05Id -import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.NamecoinNameResolver import com.vitorpamplona.quartz.nip10Notes.content.findHashtags import com.vitorpamplona.quartz.nip19Bech32.Nip19Parser import com.vitorpamplona.quartz.nip19Bech32.entities.NProfile @@ -59,6 +57,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted.Companion.WhileSubscribed +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.distinctUntilChanged @@ -68,26 +67,6 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update -/** Returns a [Nip05Id] for identifiers that should go through NIP-05 / Namecoin resolution. */ -private fun toNip05IdOrNull(term: String): Nip05Id? = - when { - term.contains('@') -> { - Nip05Id.parse(term) - } - - NamecoinNameResolver.isNamecoinIdentifier(term) -> { - if (term.endsWith(".bit", ignoreCase = true)) { - Nip05Id("_", term.lowercase()) - } else { - Nip05Id("_", term.lowercase()) - } - } - - else -> { - null - } - } - @Stable @OptIn(FlowPreview::class) class SearchBarViewModel( @@ -101,9 +80,6 @@ class SearchBarViewModel( val invalidations = MutableStateFlow(0) val searchValueFlow = MutableStateFlow("") - /** Tracks Namecoin resolution status for the UI. */ - val namecoinState = MutableStateFlow(NamecoinResolutionState.Idle) - val searchTerm = searchValueFlow .debounce(300) @@ -115,16 +91,27 @@ class SearchBarViewModel( val listState: LazyListState = LazyListState(0, 0) + /** The user resolved via Namecoin (.bit), if any, for the current search. */ + private val _namecoinResolvedUser = MutableStateFlow(null) + val namecoinResolvedUser: StateFlow = _namecoinResolvedUser + @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class) val directNip05Resolver: Flow = searchTerm .debounce(400) .mapLatest { term -> - val nip05 = toNip05IdOrNull(term) + // NIP-05 resolution: user@domain or bare .bit domain + val nip05 = + if (term.contains('@')) { + Nip05Id.parse(term) + } else if (term.endsWith(".bit", ignoreCase = true)) { + // Bare .bit domain → synthesize _@domain.bit + Nip05Id("_", term.lowercase()) + } else { + null + } + val isNamecoin = nip05 != null && nip05.domain.endsWith(".bit", ignoreCase = true) if (nip05 != null) { - val isNamecoin = NamecoinNameResolver.isNamecoinIdentifier(nip05.toValue()) - if (isNamecoin) namecoinState.emit(NamecoinResolutionState.Resolving) - val user = runCatching { nip05Client.get(nip05)?.let { info -> @@ -139,19 +126,10 @@ class SearchBarViewModel( u } }.getOrNull() - - if (isNamecoin) { - if (user != null) { - namecoinState.emit(NamecoinResolutionState.Resolved(user, term)) - } else { - namecoinState.emit(NamecoinResolutionState.Error("Could not resolve $term via Namecoin")) - } - } else { - namecoinState.emit(NamecoinResolutionState.Idle) - } + _namecoinResolvedUser.value = if (isNamecoin) user else null user } else if (term.startsWithAny(userUriPrefixes)) { - namecoinState.emit(NamecoinResolutionState.Idle) + _namecoinResolvedUser.value = null runCatching { Nip19Parser.uriToRoute(term)?.entity?.let { parsed -> when (parsed) { @@ -178,10 +156,10 @@ class SearchBarViewModel( } }.getOrNull() } else if (term.length == 64 && Hex.isHex64(term)) { - namecoinState.emit(NamecoinResolutionState.Idle) + _namecoinResolvedUser.value = null account.cache.getOrCreateUser(term) } else { - namecoinState.emit(NamecoinResolutionState.Idle) + _namecoinResolvedUser.value = null null } }.flowOn(Dispatchers.IO) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/search/SearchScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/search/SearchScreen.kt index e626a094c..17f68094a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/search/SearchScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/search/SearchScreen.kt @@ -20,29 +20,20 @@ */ package com.vitorpamplona.amethyst.ui.screen.loggedIn.search -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.expandVertically -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut -import androidx.compose.animation.shrinkVertically import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.statusBarsPadding -import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme @@ -76,7 +67,6 @@ import com.vitorpamplona.amethyst.ui.note.ClearTextIcon import com.vitorpamplona.amethyst.ui.note.NoteCompose import com.vitorpamplona.amethyst.ui.note.SearchIcon import com.vitorpamplona.amethyst.ui.note.UserCompose -import com.vitorpamplona.amethyst.ui.note.creators.userSuggestions.NamecoinResolutionState import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.ChannelName import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoClickableRow @@ -170,10 +160,7 @@ private fun SearchBar( } } - Column { - SearchTextField(searchBarViewModel, Modifier.statusBarsPadding()) - SearchNamecoinStatusBanner(searchBarViewModel) - } + SearchTextField(searchBarViewModel, Modifier.statusBarsPadding()) } @Composable @@ -229,88 +216,6 @@ private fun SearchTextField( } } -@Composable -private fun SearchNamecoinStatusBanner(searchBarViewModel: SearchBarViewModel) { - val ncState by searchBarViewModel.namecoinState.collectAsStateWithLifecycle() - - AnimatedVisibility( - visible = ncState !is NamecoinResolutionState.Idle, - enter = fadeIn() + expandVertically(), - exit = fadeOut() + shrinkVertically(), - ) { - when (val state = ncState) { - is NamecoinResolutionState.Resolving -> { - Row( - modifier = - Modifier - .fillMaxWidth() - .padding(horizontal = 14.dp, vertical = 4.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - CircularProgressIndicator( - modifier = Modifier.size(14.dp), - strokeWidth = 2.dp, - color = MaterialTheme.colorScheme.primary, - ) - Spacer(Modifier.width(8.dp)) - Text( - "Resolving via Namecoin blockchain\u2026", - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.primary, - ) - } - } - - is NamecoinResolutionState.Resolved -> { - Row( - modifier = - Modifier - .fillMaxWidth() - .padding(horizontal = 14.dp, vertical = 4.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - "\u26D3\uFE0F", - style = MaterialTheme.typography.bodySmall, - ) - Spacer(Modifier.width(6.dp)) - Text( - "Resolved via Namecoin: ${state.namecoinName}", - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.primary, - fontWeight = FontWeight.Medium, - ) - } - } - - is NamecoinResolutionState.Error -> { - Row( - modifier = - Modifier - .fillMaxWidth() - .padding(horizontal = 14.dp, vertical = 4.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - "\u26A0\uFE0F", - style = MaterialTheme.typography.bodySmall, - ) - Spacer(Modifier.width(6.dp)) - Text( - state.message, - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.error, - ) - } - } - - is NamecoinResolutionState.Idle -> { - // nothing - } - } - } -} - @Composable private fun DisplaySearchResults( searchBarViewModel: SearchBarViewModel, @@ -324,11 +229,11 @@ private fun DisplaySearchResults( val hashTags by searchBarViewModel.hashtagResults.collectAsStateWithLifecycle() val relays by searchBarViewModel.relayResults.collectAsStateWithLifecycle() val users by searchBarViewModel.searchResultsUsers.collectAsStateWithLifecycle() + val namecoinUser by searchBarViewModel.namecoinResolvedUser.collectAsStateWithLifecycle() val publicChatChannels by searchBarViewModel.searchResultsPublicChatChannels.collectAsStateWithLifecycle() val ephemeralChannels by searchBarViewModel.searchResultsEphemeralChannels.collectAsStateWithLifecycle() val liveActivityChannels by searchBarViewModel.searchResultsLiveActivityChannels.collectAsStateWithLifecycle() val notes by searchBarViewModel.searchResultsNotes.collectAsStateWithLifecycle() - val ncState by searchBarViewModel.namecoinState.collectAsStateWithLifecycle() LazyColumn( modifier = Modifier.fillMaxHeight(), @@ -351,22 +256,14 @@ private fun DisplaySearchResults( users, key = { _, item -> "u" + item.pubkeyHex }, ) { _, item -> - val isNamecoinResult = - ncState is NamecoinResolutionState.Resolved && - (ncState as NamecoinResolutionState.Resolved).user == item - - if (isNamecoinResult) { - Row( + if (namecoinUser == item) { + Text( + "\u26D3\uFE0F Namecoin", + style = MaterialTheme.typography.labelSmall, + color = MaterialTheme.colorScheme.primary, + fontWeight = FontWeight.Medium, modifier = Modifier.padding(horizontal = 12.dp, vertical = 2.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - "\u26D3\uFE0F Namecoin result", - style = MaterialTheme.typography.labelSmall, - color = MaterialTheme.colorScheme.primary, - fontWeight = FontWeight.Medium, - ) - } + ) } UserCompose(item, accountViewModel = accountViewModel, nav = nav) diff --git a/amethyst/src/main/res/values/strings.xml b/amethyst/src/main/res/values/strings.xml index 38c3c142f..ec51b889a 100644 --- a/amethyst/src/main/res/values/strings.xml +++ b/amethyst/src/main/res/values/strings.xml @@ -1820,7 +1820,7 @@ Select Users to Follow Profile to import from search, npub1…, alice@example.com - Supports npub, nprofile, NIP-05, hex, and namecoin (.bit, d/, id/) + Supports npub, nprofile, NIP-05, hex, and Namecoin (.bit) Look Up Follow List Tip %1$d accounts found