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:
+36
-2
@@ -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())
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user