feat: wire Namecoin search into SearchBarViewModel

Resolve .bit, d/, and id/ identifiers from the search bar via
ElectrumX blockchain lookups. Typing any Namecoin identifier format
(m@testls.bit, testls.bit, d/testls, id/alice) now queries the
Namecoin blockchain and shows the resolved user at the top of results.

The namecoinResolvedUser flow in SearchBarViewModel detects Namecoin
identifiers, resolves them through NamecoinNameService, and prepends
the result to the standard local cache search results.

Updated docs with search integration details and manual testing guide.
This commit is contained in:
M
2026-03-02 19:43:24 +11:00
parent d82ace2f63
commit f6447d2020
2 changed files with 80 additions and 8 deletions
@@ -32,8 +32,11 @@ import androidx.lifecycle.viewModelScope
import com.vitorpamplona.amethyst.commons.ui.feeds.InvalidatableContent
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.namecoin.NamecoinNameService
import com.vitorpamplona.amethyst.service.relayClient.searchCommand.SearchQueryState
import com.vitorpamplona.amethyst.ui.dal.DefaultFeedOrder
import com.vitorpamplona.quartz.nip05.namecoin.NamecoinNameResolver
import com.vitorpamplona.quartz.nip10Notes.content.findHashtags
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.FlowPreview
@@ -43,7 +46,9 @@ import kotlinx.coroutines.flow.SharingStarted.Companion.WhileSubscribed
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
@@ -69,12 +74,41 @@ class SearchBarViewModel(
val searchDataSourceState = SearchQueryState(MutableStateFlow(searchValue), account)
/**
* Resolves Namecoin identifiers (.bit / d/ / id/) via ElectrumX and
* returns the matching [User] from LocalCache, or null.
*/
private val namecoinResolvedUser =
searchValueFlow
.debounce(400)
.distinctUntilChanged()
.filter { NamecoinNameResolver.isNamecoinIdentifier(it) }
.map { term ->
try {
val result = NamecoinNameService.getInstance().resolve(term)
if (result != null) {
LocalCache.getOrCreateUser(result.pubkey)
} else {
null
}
} catch (_: Exception) {
null
}
}.flowOn(Dispatchers.IO)
.stateIn(viewModelScope, WhileSubscribed(5000), null)
val searchResultsUsers =
combine(
searchValueFlow.debounce(100),
invalidations.debounce(100),
) { term, version ->
LocalCache.findUsersStartingWith(term, account)
namecoinResolvedUser,
) { term, version, namecoinUser ->
val localResults = LocalCache.findUsersStartingWith(term, account)
if (namecoinUser != null && localResults.none { it.pubkeyHex == namecoinUser.pubkeyHex }) {
listOf(namecoinUser) + localResults
} else {
localResults
}
}.flowOn(Dispatchers.IO)
.stateIn(viewModelScope, WhileSubscribed(5000), emptyList())
+44 -6
View File
@@ -154,6 +154,17 @@ The integration is minimal and non-invasive:
3. Non-Namecoin identifiers are completely unaffected
4. **`AppModules`** wires up the resolver at construction time
## Search Integration
The search bar resolves Namecoin identifiers in real-time via `SearchBarViewModel`:
1. A `namecoinResolvedUser` flow watches the search input with a 400ms debounce
2. If the input matches any Namecoin format (`d/*`, `id/*`, `*.bit`, `*@*.bit`), it resolves via `NamecoinNameService``ElectrumxClient` → blockchain
3. The resolved pubkey is used to get/create a `User` in `LocalCache`
4. The Namecoin-resolved user is prepended to the standard local search results (deduplicated)
This means typing `alice@example.bit`, `example.bit`, `d/example`, or `id/alice` into the search bar will query the Namecoin blockchain and show the resolved user profile at the top of results.
## Default ElectrumX Server
```
@@ -202,6 +213,7 @@ Search bar integration. When a user types a `.bit` identifier, shows a loading s
### Modified files
- `amethyst/.../AppModules.kt` — Wire up `NamecoinNameResolver` into `Nip05Client`
- `amethyst/.../ui/screen/loggedIn/search/SearchBarViewModel.kt` — Namecoin search resolution
- `quartz/.../nip05DnsIdentifiers/Nip05Client.kt` — Route `.bit` identifiers to Namecoin resolver
- `amethyst/.../relays/RelayInformationScreen.kt` — Import reordering (spotless)
@@ -212,13 +224,34 @@ Search bar integration. When a user types a `.bit` identifier, shows a loading s
./gradlew :quartz:jvmTest --tests "*NamecoinNameResolverTest*"
```
### Manual testing (emulator)
1. Build and install: `./gradlew :amethyst:installFdroidDebug`
2. Search for `m@testls.bit` — should resolve to pubkey `6cdebcca...18667d`
3. Search for `testls.bit` — root domain lookup
4. Search for `d/testls` — direct namespace format
### Manual testing (emulator or device)
### Live verification
Build and install the debug APK:
```bash
./gradlew assemblePlayDebug
adb install -r amethyst/build/outputs/apk/play/debug/amethyst-play-universal-debug.apk
```
**Search bar tests** — open the search bar and enter each of these:
| Search query | Expected result | What it tests |
|---|---|---|
| `m@testls.bit` | Resolves to Vitor Pamplona's profile | NIP-05 style `user@domain.bit` |
| `testls.bit` | Resolves to Vitor Pamplona's profile (root `_` entry) | Bare domain `.bit` lookup |
| `d/testls` | Resolves to Vitor Pamplona's profile | Direct `d/` namespace |
| `id/someuser` | Resolves if registered on-chain | Direct `id/` namespace |
**Verification test** — if a profile has a `.bit` address in its `nip05` field, the NIP-05 badge should verify via the blockchain instead of HTTP.
**Network verification** — to confirm ElectrumX calls are being made:
```bash
# Monitor traffic to ElectrumX ports on the emulator
adb root
adb shell tcpdump -i any -nn port 50002 or port 50006
```
You should see TCP connections to `162.212.154.52:50002` (electrumx.testls.space) when searching for `.bit` identifiers.
### Live test data
The name `d/testls` is registered on the Namecoin blockchain (block 551519+, last updated block 814278) with value:
```json
{
@@ -229,3 +262,8 @@ The name `d/testls` is registered on the Namecoin blockchain (block 551519+, las
}
}
```
This means:
- `m@testls.bit` → resolves `m` entry → pubkey `6cdebcca...18667d`
- `testls.bit` → resolves root `_` entry → falls back to first available entry
- `d/testls` → same as `testls.bit` (root lookup)