fix: add missing StateFlow import, merge duplicate KDoc blocks, and fix rebase conflicts

- Add missing 'import kotlinx.coroutines.flow.StateFlow' in SearchBarViewModel.kt
- Merge duplicate KDoc blocks on nameShowWithFallback in ElectrumXClient.kt
- Move NamecoinLookupException to ElectrumXServer.kt (avoid redeclaration)
- Remove duplicate NameShowResult/ElectrumxServer from ElectrumXClient.kt
- Fix ElectrumxClient -> ElectrumXClient renames in NamecoinNameService
- Extract namecoinElectrumxClient in AppModules.kt for NamecoinNameService.init()
- Update package paths from nip05.namecoin to nip05DnsIdentifiers.namecoin
This commit is contained in:
M
2026-03-06 14:13:25 +11:00
parent 050fd3a412
commit 4ecfbbb844
6 changed files with 52 additions and 63 deletions
@@ -147,12 +147,14 @@ class AppModules(
// Custom fetcher that considers tor settings and avoids forwarding.
val nip05Fetcher = OkHttpNip05Fetcher(roleBasedHttpClientBuilder::okHttpClientForNip05)
val namecoinResolver =
NamecoinNameResolver(
electrumxClient =
val namecoinElectrumxClient =
ElectrumXClient(
socketFactory = { roleBasedHttpClientBuilder.socketFactoryForNip05() },
),
)
val namecoinResolver =
NamecoinNameResolver(
electrumxClient = namecoinElectrumxClient,
serverListProvider = {
if (roleBasedHttpClientBuilder.shouldUseTorForNIP05("https://electrumx.example.com")) {
TOR_ELECTRUMX_SERVERS
@@ -20,6 +20,7 @@
*/
package com.vitorpamplona.amethyst.service.namecoin
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.DEFAULT_ELECTRUMX_SERVERS
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.ElectrumXClient
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.ElectrumxServer
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.NamecoinLookupCache
@@ -50,7 +51,7 @@ class NamecoinNameService(
private val resolver =
NamecoinNameResolver(
electrumxClient = electrumxClient,
serverListProvider = { customServers.ifEmpty { ElectrumxClient.DEFAULT_SERVERS } },
serverListProvider = { customServers.ifEmpty { DEFAULT_ELECTRUMX_SERVERS } },
)
private val cache = NamecoinLookupCache()
@@ -63,7 +64,7 @@ class NamecoinNameService(
"NamecoinNameService not initialized. Call init() first.",
)
fun init(electrumxClient: ElectrumxClient): NamecoinNameService =
fun init(electrumxClient: ElectrumXClient): NamecoinNameService =
synchronized(this) {
instance?.let { return it }
NamecoinNameService(electrumxClient).also { instance = it }
@@ -34,10 +34,9 @@ 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.quartz.nip05.namecoin.NamecoinLookupException
import com.vitorpamplona.amethyst.service.relayClient.searchCommand.SearchQueryState
import com.vitorpamplona.amethyst.ui.dal.DefaultFeedOrder
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.NamecoinLookupException
import com.vitorpamplona.quartz.nip05DnsIdentifiers.namecoin.NamecoinNameResolver
import com.vitorpamplona.quartz.nip10Notes.content.findHashtags
import kotlinx.coroutines.Dispatchers
@@ -45,6 +44,7 @@ import kotlinx.coroutines.FlowPreview
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
@@ -83,10 +83,21 @@ class SearchBarViewModel(
*/
sealed class NamecoinSearchState {
data object Idle : NamecoinSearchState()
data object Loading : NamecoinSearchState()
data class Resolved(val user: User) : NamecoinSearchState()
data class NotFound(val name: String) : NamecoinSearchState()
data class Expired(val name: String) : NamecoinSearchState()
data class Resolved(
val user: User,
) : NamecoinSearchState()
data class NotFound(
val name: String,
) : NamecoinSearchState()
data class Expired(
val name: String,
) : NamecoinSearchState()
data object ServersUnreachable : NamecoinSearchState()
}
@@ -132,8 +143,7 @@ class SearchBarViewModel(
is NamecoinSearchState.Resolved -> state.user
else -> null
}
}
.stateIn(viewModelScope, WhileSubscribed(5000), null)
}.stateIn(viewModelScope, WhileSubscribed(5000), null)
val searchResultsUsers =
combine(
@@ -238,16 +238,14 @@ private fun ReactionRowItemCard(
.fillMaxWidth()
.onGloballyPositioned { coordinates ->
onMeasured(coordinates.size.height.toFloat())
}
.graphicsLayer {
}.graphicsLayer {
translationY = dragOffsetY
shadowElevation = elevation
if (isDragging) {
scaleX = 1.02f
scaleY = 1.02f
}
}
.padding(vertical = 8.dp),
}.padding(vertical = 8.dp),
) {
Row(
modifier = Modifier.fillMaxWidth(),
@@ -48,6 +48,30 @@ data class ElectrumxServer(
val trustAllCerts: Boolean = false,
)
/**
* Specific exception types for Namecoin resolution failures.
* Allows callers to distinguish "name doesn't exist" from "servers unreachable".
*/
sealed class NamecoinLookupException(
message: String,
cause: Throwable? = null,
) : Exception(message, cause) {
/** The name was queried successfully but does not exist on the blockchain. */
class NameNotFound(
val name: String,
) : NamecoinLookupException("Name not found: $name")
/** The name has expired (>36000 blocks since last update). */
class NameExpired(
val name: String,
) : NamecoinLookupException("Name expired: $name")
/** All ElectrumX servers were unreachable or returned errors. */
class ServersUnreachable(
val lastError: Throwable? = null,
) : NamecoinLookupException("All ElectrumX servers unreachable", lastError)
}
/** Well-known public Namecoin ElectrumX servers (clearnet). */
val DEFAULT_ELECTRUMX_SERVERS =
listOf(
@@ -52,48 +52,6 @@ import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager
/**
* Result of an ElectrumX name_show query.
*
* Maps to the JSON fields returned by Namecoin Core / Electrum-NMC:
* { "name": "d/example", "value": "{...}", "txid": "abc...", "height": 12345, ... }
*/
@Serializable
data class NameShowResult(
val name: String,
val value: String,
val txid: String? = null,
val height: Int? = null,
val expiresIn: Int? = null,
)
/**
* Specific exception types for Namecoin resolution failures.
* Allows callers to distinguish "name doesn't exist" from "servers unreachable".
*/
sealed class NamecoinLookupException(message: String, cause: Throwable? = null) : Exception(message, cause) {
/** The name was queried successfully but does not exist on the blockchain. */
class NameNotFound(val name: String) : NamecoinLookupException("Name not found: $name")
/** The name has expired (>36000 blocks since last update). */
class NameExpired(val name: String) : NamecoinLookupException("Name expired: $name")
/** All ElectrumX servers were unreachable or returned errors. */
class ServersUnreachable(val lastError: Throwable? = null) :
NamecoinLookupException("All ElectrumX servers unreachable", lastError)
}
/**
* Represents a single ElectrumX server endpoint.
*/
data class ElectrumxServer(
val host: String,
val port: Int,
val useSsl: Boolean = true,
/** If true, accept any certificate (self-signed, expired, etc.) */
val trustAllCerts: Boolean = false,
)
/**
* Lightweight, query-only ElectrumX client for Namecoin name resolution.
*
@@ -184,10 +142,6 @@ class ElectrumXClient(
*
* @param identifier Full Namecoin name, e.g. "d/example"
* @param servers Ordered server list to try; defaults to [DEFAULT_ELECTRUMX_SERVERS]
*/
/**
* Try each server in order until one succeeds.
*
* @throws NamecoinLookupException.NameNotFound if the name definitively doesn't exist
* @throws NamecoinLookupException.NameExpired if the name has expired
* @throws NamecoinLookupException.ServersUnreachable if all servers failed with connection errors