refactor: integrate relay event counts into each ViewModel

Move count query logic from separate RelayEventCountViewModel into each
relay list's own ViewModel. BasicRelaySetupInfoModel now manages counts
via countFilters() override. Nip65RelayListViewModel gets its own count
infrastructure for home (outbox) and notification (inbox) relay lists.
Simplify AllRelayListScreen by removing 7 extra count ViewModels and
their LaunchedEffects.

https://claude.ai/code/session_016158D5mq5BygS1uBbLNbsA
This commit is contained in:
Claude
2026-03-15 15:02:09 +00:00
parent b1723a5dfb
commit 764c2241ca
9 changed files with 261 additions and 262 deletions
@@ -60,7 +60,6 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.blocked.BlockedRelay
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.blocked.renderBlockedItems
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.broadcast.BroadcastRelayListViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.broadcast.renderBroadcastItems
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.RelayEventCountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.RelayExporter
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.RelayListCollection
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.RelayZipExporter
@@ -110,13 +109,6 @@ fun AllRelayListScreen(
val indexerViewModel: IndexerRelayListViewModel = viewModel()
val proxyViewModel: ProxyRelayListViewModel = viewModel()
val relayFeedsViewModel: RelayFeedsListViewModel = viewModel()
val outboxCountViewModel: RelayEventCountViewModel = viewModel(key = "outboxCount")
val inboxCountViewModel: RelayEventCountViewModel = viewModel(key = "inboxCount")
val dmCountViewModel: RelayEventCountViewModel = viewModel(key = "dmCount")
val privateHomeCountViewModel: RelayEventCountViewModel = viewModel(key = "privateHomeCount")
val proxyCountViewModel: RelayEventCountViewModel = viewModel(key = "proxyCount")
val indexerCountViewModel: RelayEventCountViewModel = viewModel(key = "indexerCount")
val searchCountViewModel: RelayEventCountViewModel = viewModel(key = "searchCount")
dmViewModel.init(accountViewModel)
nip65ViewModel.init(accountViewModel)
@@ -159,13 +151,6 @@ fun AllRelayListScreen(
indexerViewModel,
proxyViewModel,
relayFeedsViewModel,
outboxCountViewModel,
inboxCountViewModel,
dmCountViewModel,
privateHomeCountViewModel,
proxyCountViewModel,
indexerCountViewModel,
searchCountViewModel,
accountViewModel,
nav,
)
@@ -186,13 +171,6 @@ fun MappedAllRelayListView(
indexerViewModel: IndexerRelayListViewModel,
proxyViewModel: ProxyRelayListViewModel,
relayFeedsViewModel: RelayFeedsListViewModel,
outboxCountViewModel: RelayEventCountViewModel,
inboxCountViewModel: RelayEventCountViewModel,
dmCountViewModel: RelayEventCountViewModel,
privateHomeCountViewModel: RelayEventCountViewModel,
proxyCountViewModel: RelayEventCountViewModel,
indexerCountViewModel: RelayEventCountViewModel,
searchCountViewModel: RelayEventCountViewModel,
accountViewModel: AccountViewModel,
nav: INav,
) {
@@ -210,71 +188,13 @@ fun MappedAllRelayListView(
val proxyRelays by proxyViewModel.relays.collectAsStateWithLifecycle()
val relayFeedsFeedState by relayFeedsViewModel.relays.collectAsStateWithLifecycle()
val outboxCounts by outboxCountViewModel.counts.collectAsStateWithLifecycle()
val inboxCounts by inboxCountViewModel.counts.collectAsStateWithLifecycle()
val dmCounts by dmCountViewModel.counts.collectAsStateWithLifecycle()
val privateHomeCounts by privateHomeCountViewModel.counts.collectAsStateWithLifecycle()
val proxyCounts by proxyCountViewModel.counts.collectAsStateWithLifecycle()
val indexerCounts by indexerCountViewModel.counts.collectAsStateWithLifecycle()
val searchCounts by searchCountViewModel.counts.collectAsStateWithLifecycle()
val userPubKey = accountViewModel.account.pubKey
LaunchedEffect(homeFeedState) {
if (homeFeedState.isNotEmpty()) {
outboxCountViewModel.queryCountsForRelays(
RelayEventCountViewModel.authorCountFilters(userPubKey, homeFeedState.map { it.relay }),
)
}
}
LaunchedEffect(notifFeedState) {
if (notifFeedState.isNotEmpty()) {
inboxCountViewModel.queryCountsForRelays(
RelayEventCountViewModel.pTagCountFilters(userPubKey, notifFeedState.map { it.relay }),
)
}
}
LaunchedEffect(dmFeedState) {
if (dmFeedState.isNotEmpty()) {
dmCountViewModel.queryCountsForRelays(
RelayEventCountViewModel.dmCountFilters(userPubKey, dmFeedState.map { it.relay }),
)
}
}
LaunchedEffect(privateOutboxFeedState) {
if (privateOutboxFeedState.isNotEmpty()) {
privateHomeCountViewModel.queryCountsForRelays(
RelayEventCountViewModel.authorCountFilters(userPubKey, privateOutboxFeedState.map { it.relay }),
)
}
}
LaunchedEffect(proxyRelays) {
if (proxyRelays.isNotEmpty()) {
proxyCountViewModel.queryCountsForRelays(
RelayEventCountViewModel.totalCountFilters(proxyRelays.map { it.relay }),
)
}
}
LaunchedEffect(indexerRelays) {
if (indexerRelays.isNotEmpty()) {
indexerCountViewModel.queryCountsForRelays(
RelayEventCountViewModel.indexerCountFilters(indexerRelays.map { it.relay }),
)
}
}
LaunchedEffect(searchFeedState) {
if (searchFeedState.isNotEmpty()) {
searchCountViewModel.queryCountsForRelays(
RelayEventCountViewModel.totalCountFilters(searchFeedState.map { it.relay }),
)
}
}
val outboxCounts by nip65ViewModel.homeCountResults.collectAsStateWithLifecycle()
val inboxCounts by nip65ViewModel.notifCountResults.collectAsStateWithLifecycle()
val dmCounts by dmViewModel.countResults.collectAsStateWithLifecycle()
val privateHomeCounts by privateOutboxViewModel.countResults.collectAsStateWithLifecycle()
val proxyCounts by proxyViewModel.countResults.collectAsStateWithLifecycle()
val indexerCounts by indexerViewModel.countResults.collectAsStateWithLifecycle()
val searchCounts by searchViewModel.countResults.collectAsStateWithLifecycle()
Scaffold(
topBar = {
@@ -26,6 +26,11 @@ import com.vitorpamplona.amethyst.Amethyst
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.service.replace
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.IRelayClientListener
import com.vitorpamplona.quartz.nip01Core.relay.client.single.IRelayClient
import com.vitorpamplona.quartz.nip01Core.relay.client.single.newSubId
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.CountMessage
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.Message
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
@@ -33,13 +38,19 @@ import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
abstract class BasicRelaySetupInfoModel : ViewModel() {
abstract class BasicRelaySetupInfoModel : ViewModel(), IRelayClientListener {
lateinit var accountViewModel: AccountViewModel
lateinit var account: Account
private val _relays = MutableStateFlow<List<BasicRelaySetupInfo>>(emptyList())
val relays = _relays.asStateFlow()
private val _countResults = MutableStateFlow<Map<NormalizedRelayUrl, RelayCountResult>>(emptyMap())
val countResults = _countResults.asStateFlow()
private val subIdToRelay = mutableMapOf<String, Pair<NormalizedRelayUrl, Int>>()
private val relayQueryInfos = mutableMapOf<NormalizedRelayUrl, List<CountQueryInfo>>()
var hasModified = false
fun init(accountViewModel: AccountViewModel) {
@@ -50,12 +61,15 @@ abstract class BasicRelaySetupInfoModel : ViewModel() {
fun load() {
clear()
loadRelayDocuments()
loadCounts()
}
abstract fun getRelayList(): List<NormalizedRelayUrl>?
abstract suspend fun saveRelayList(urlList: List<NormalizedRelayUrl>)
open fun countFilters(relayUrl: NormalizedRelayUrl): List<CountFilter> = emptyList()
fun create() {
if (hasModified) {
accountViewModel.launchSigner {
@@ -79,6 +93,83 @@ abstract class BasicRelaySetupInfoModel : ViewModel() {
}
}
private fun loadCounts() {
val client = Amethyst.instance.client
cleanupCounts()
val relayList = _relays.value
if (relayList.isEmpty()) return
val hasFilters = relayList.any { countFilters(it.relay).isNotEmpty() }
if (!hasFilters) return
client.subscribe(this)
relayList.forEach { item ->
val filters = countFilters(item.relay)
if (filters.isEmpty()) return@forEach
val queryInfos = mutableListOf<CountQueryInfo>()
filters.forEachIndexed { index, countFilter ->
val subId = newSubId()
subIdToRelay[subId] = Pair(item.relay, index)
queryInfos.add(CountQueryInfo(subId, countFilter.label, index))
client.queryCount(
subId = subId,
filters = mapOf(item.relay to listOf(countFilter.filter)),
)
}
relayQueryInfos[item.relay] = queryInfos
}
}
override fun onIncomingMessage(
relay: IRelayClient,
msgStr: String,
msg: Message,
) {
if (msg is CountMessage) {
val (relayUrl, filterIndex) = subIdToRelay[msg.queryId] ?: return
val queryInfos = relayQueryInfos[relayUrl] ?: return
val queryInfo = queryInfos.find { it.filterIndex == filterIndex } ?: return
_countResults.update { currentMap ->
val currentResult = currentMap[relayUrl] ?: RelayCountResult()
val updatedEntries = currentResult.counts.toMutableList()
val existingIndex = updatedEntries.indexOfFirst { it.label == queryInfo.label }
val newEntry =
RelayCountResult.CountEntry(
label = queryInfo.label,
count = msg.result.count,
approximate = msg.result.approximate,
)
if (existingIndex >= 0) {
updatedEntries[existingIndex] = newEntry
} else {
updatedEntries.add(newEntry)
}
currentMap + (relayUrl to RelayCountResult(updatedEntries))
}
}
}
private fun cleanupCounts() {
val client = Amethyst.instance.client
subIdToRelay.keys.forEach { subId ->
client.close(subId)
}
subIdToRelay.clear()
relayQueryInfos.clear()
_countResults.value = emptyMap()
client.unsubscribe(this)
}
open fun relayListBuilder(): List<BasicRelaySetupInfo> {
val relayList = getRelayList() ?: emptyList()
@@ -122,4 +213,15 @@ abstract class BasicRelaySetupInfoModel : ViewModel() {
) {
_relays.update { it.replace(relay, relay.copy(paidRelay = paid)) }
}
override fun onCleared() {
cleanupCounts()
super.onCleared()
}
private data class CountQueryInfo(
val subId: String,
val label: String,
val filterIndex: Int,
)
}
@@ -21,21 +21,7 @@
package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common
import androidx.compose.runtime.Immutable
import androidx.lifecycle.ViewModel
import com.vitorpamplona.amethyst.Amethyst
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.IRelayClientListener
import com.vitorpamplona.quartz.nip01Core.relay.client.single.IRelayClient
import com.vitorpamplona.quartz.nip01Core.relay.client.single.newSubId
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.CountMessage
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.Message
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
@Immutable
data class RelayCountResult(
@@ -49,165 +35,6 @@ data class RelayCountResult(
)
}
class RelayEventCountViewModel : ViewModel(), IRelayClientListener {
private val client: INostrClient get() = Amethyst.instance.client
private val _counts = MutableStateFlow<Map<NormalizedRelayUrl, RelayCountResult>>(emptyMap())
val counts = _counts.asStateFlow()
// Maps subId -> (relay, filterIndex) so we can route count results
private val subIdToRelay = mutableMapOf<String, Pair<NormalizedRelayUrl, Int>>()
// Maps relay -> list of (subId, label) to track active queries
private val relayQueries = mutableMapOf<NormalizedRelayUrl, MutableList<QueryInfo>>()
private data class QueryInfo(
val subId: String,
val label: String,
val filterIndex: Int,
)
fun queryCountsForRelays(queries: Map<NormalizedRelayUrl, List<CountFilter>>) {
cleanup()
client.subscribe(this)
queries.forEach { (relay, filters) ->
val queryInfos = mutableListOf<QueryInfo>()
filters.forEachIndexed { index, countFilter ->
val subId = newSubId()
subIdToRelay[subId] = Pair(relay, index)
queryInfos.add(QueryInfo(subId, countFilter.label, index))
client.queryCount(
subId = subId,
filters = mapOf(relay to listOf(countFilter.filter)),
)
}
relayQueries[relay] = queryInfos
}
}
override fun onIncomingMessage(
relay: IRelayClient,
msgStr: String,
msg: Message,
) {
if (msg is CountMessage) {
val (relayUrl, filterIndex) = subIdToRelay[msg.queryId] ?: return
val queryInfos = relayQueries[relayUrl] ?: return
val queryInfo = queryInfos.find { it.filterIndex == filterIndex } ?: return
_counts.update { currentMap ->
val currentResult = currentMap[relayUrl] ?: RelayCountResult()
val updatedEntries = currentResult.counts.toMutableList()
// Replace or add the entry for this filter
val existingIndex = updatedEntries.indexOfFirst { it.label == queryInfo.label }
val newEntry =
RelayCountResult.CountEntry(
label = queryInfo.label,
count = msg.result.count,
approximate = msg.result.approximate,
)
if (existingIndex >= 0) {
updatedEntries[existingIndex] = newEntry
} else {
updatedEntries.add(newEntry)
}
currentMap + (relayUrl to RelayCountResult(updatedEntries))
}
}
}
private fun cleanup() {
// Close existing queries
subIdToRelay.keys.forEach { subId ->
client.close(subId)
}
subIdToRelay.clear()
relayQueries.clear()
_counts.value = emptyMap()
client.unsubscribe(this)
}
override fun onCleared() {
cleanup()
super.onCleared()
}
companion object {
fun authorCountFilters(
userPubKey: HexKey,
relays: List<NormalizedRelayUrl>,
): Map<NormalizedRelayUrl, List<CountFilter>> =
relays.associateWith {
listOf(
CountFilter(
label = "events",
filter = Filter(authors = listOf(userPubKey)),
),
)
}
fun pTagCountFilters(
userPubKey: HexKey,
relays: List<NormalizedRelayUrl>,
): Map<NormalizedRelayUrl, List<CountFilter>> =
relays.associateWith {
listOf(
CountFilter(
label = "events",
filter = Filter(tags = mapOf("p" to listOf(userPubKey))),
),
)
}
fun dmCountFilters(
userPubKey: HexKey,
relays: List<NormalizedRelayUrl>,
): Map<NormalizedRelayUrl, List<CountFilter>> =
relays.associateWith {
listOf(
CountFilter(
label = "events",
filter =
Filter(
kinds = listOf(GiftWrapEvent.KIND, PrivateDmEvent.KIND),
tags = mapOf("p" to listOf(userPubKey)),
),
),
)
}
fun totalCountFilters(relays: List<NormalizedRelayUrl>): Map<NormalizedRelayUrl, List<CountFilter>> =
relays.associateWith {
listOf(
CountFilter(
label = "events",
filter = Filter(kinds = null),
),
)
}
fun indexerCountFilters(relays: List<NormalizedRelayUrl>): Map<NormalizedRelayUrl, List<CountFilter>> =
relays.associateWith {
listOf(
CountFilter(
label = "kind 0",
filter = Filter(kinds = listOf(0)),
),
CountFilter(
label = "kind 10002",
filter = Filter(kinds = listOf(10002)),
),
)
}
}
}
data class CountFilter(
val label: String,
val filter: Filter,
@@ -22,7 +22,11 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.dm
import androidx.compose.runtime.Stable
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.CountFilter
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
@Stable
class DMRelayListViewModel : BasicRelaySetupInfoModel() {
@@ -31,4 +35,16 @@ class DMRelayListViewModel : BasicRelaySetupInfoModel() {
override suspend fun saveRelayList(urlList: List<NormalizedRelayUrl>) {
account.saveDMRelayList(urlList)
}
override fun countFilters(relayUrl: NormalizedRelayUrl): List<CountFilter> =
listOf(
CountFilter(
label = "events",
filter =
Filter(
kinds = listOf(GiftWrapEvent.KIND, PrivateDmEvent.KIND),
tags = mapOf("p" to listOf(account.pubKey)),
),
),
)
}
@@ -22,6 +22,8 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.indexer
import androidx.compose.runtime.Stable
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.CountFilter
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
@Stable
@@ -33,4 +35,16 @@ class IndexerRelayListViewModel : BasicRelaySetupInfoModel() {
override suspend fun saveRelayList(urlList: List<NormalizedRelayUrl>) {
account.saveIndexerRelayList(urlList)
}
override fun countFilters(relayUrl: NormalizedRelayUrl): List<CountFilter> =
listOf(
CountFilter(
label = "kind 0",
filter = Filter(kinds = listOf(0)),
),
CountFilter(
label = "kind 10002",
filter = Filter(kinds = listOf(10002)),
),
)
}
@@ -22,6 +22,8 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.nip37
import androidx.compose.runtime.Stable
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.CountFilter
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
@Stable
@@ -33,4 +35,12 @@ class PrivateOutboxRelayListViewModel : BasicRelaySetupInfoModel() {
override suspend fun saveRelayList(urlList: List<NormalizedRelayUrl>) {
account.savePrivateOutboxRelayList(urlList)
}
override fun countFilters(relayUrl: NormalizedRelayUrl): List<CountFilter> =
listOf(
CountFilter(
label = "events",
filter = Filter(authors = listOf(account.pubKey)),
),
)
}
@@ -28,7 +28,15 @@ import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.service.replace
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfo
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.RelayCountResult
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.IRelayClientListener
import com.vitorpamplona.quartz.nip01Core.relay.client.single.IRelayClient
import com.vitorpamplona.quartz.nip01Core.relay.client.single.newSubId
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.CountMessage
import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.Message
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip65RelayList.tags.AdvertisedRelayInfo
import com.vitorpamplona.quartz.nip65RelayList.tags.AdvertisedRelayType
import kotlinx.coroutines.Dispatchers
@@ -38,7 +46,7 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
@Stable
class Nip65RelayListViewModel : ViewModel() {
class Nip65RelayListViewModel : ViewModel(), IRelayClientListener {
private lateinit var accountViewModel: AccountViewModel
private lateinit var account: Account
@@ -48,6 +56,14 @@ class Nip65RelayListViewModel : ViewModel() {
private val _notificationRelays = MutableStateFlow<List<BasicRelaySetupInfo>>(emptyList())
val notificationRelays = _notificationRelays.asStateFlow()
private val _homeCountResults = MutableStateFlow<Map<NormalizedRelayUrl, RelayCountResult>>(emptyMap())
val homeCountResults = _homeCountResults.asStateFlow()
private val _notifCountResults = MutableStateFlow<Map<NormalizedRelayUrl, RelayCountResult>>(emptyMap())
val notifCountResults = _notifCountResults.asStateFlow()
private val subIdToRelay = mutableMapOf<String, Pair<NormalizedRelayUrl, Boolean>>()
var hasModified = false
fun init(accountViewModel: AccountViewModel) {
@@ -58,6 +74,7 @@ class Nip65RelayListViewModel : ViewModel() {
fun load() {
clear()
loadRelayDocuments()
loadCounts()
}
fun create() {
@@ -111,6 +128,74 @@ class Nip65RelayListViewModel : ViewModel() {
}
}
private fun loadCounts() {
val client = Amethyst.instance.client
cleanupCounts()
val homeList = _homeRelays.value
val notifList = _notificationRelays.value
if (homeList.isEmpty() && notifList.isEmpty()) return
client.subscribe(this)
homeList.forEach { item ->
val subId = newSubId()
subIdToRelay[subId] = Pair(item.relay, true)
client.queryCount(
subId = subId,
filters = mapOf(item.relay to listOf(Filter(authors = listOf(account.pubKey)))),
)
}
notifList.forEach { item ->
val subId = newSubId()
subIdToRelay[subId] = Pair(item.relay, false)
client.queryCount(
subId = subId,
filters = mapOf(item.relay to listOf(Filter(tags = mapOf("p" to listOf(account.pubKey))))),
)
}
}
override fun onIncomingMessage(
relay: IRelayClient,
msgStr: String,
msg: Message,
) {
if (msg is CountMessage) {
val (relayUrl, isHome) = subIdToRelay[msg.queryId] ?: return
val newResult =
RelayCountResult(
listOf(
RelayCountResult.CountEntry(
label = "events",
count = msg.result.count,
approximate = msg.result.approximate,
),
),
)
if (isHome) {
_homeCountResults.update { it + (relayUrl to newResult) }
} else {
_notifCountResults.update { it + (relayUrl to newResult) }
}
}
}
private fun cleanupCounts() {
val client = Amethyst.instance.client
subIdToRelay.keys.forEach { subId ->
client.close(subId)
}
subIdToRelay.clear()
_homeCountResults.value = emptyMap()
_notifCountResults.value = emptyMap()
client.unsubscribe(this)
}
fun clear() {
hasModified = false
_homeRelays.update {
@@ -181,4 +266,9 @@ class Nip65RelayListViewModel : ViewModel() {
) {
_notificationRelays.update { it.replace(relay, relay.copy(paidRelay = paid)) }
}
override fun onCleared() {
cleanupCounts()
super.onCleared()
}
}
@@ -22,6 +22,8 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.proxy
import androidx.compose.runtime.Stable
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.CountFilter
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
@Stable
@@ -33,4 +35,12 @@ class ProxyRelayListViewModel : BasicRelaySetupInfoModel() {
override suspend fun saveRelayList(urlList: List<NormalizedRelayUrl>) {
account.saveProxyRelayList(urlList)
}
override fun countFilters(relayUrl: NormalizedRelayUrl): List<CountFilter> =
listOf(
CountFilter(
label = "events",
filter = Filter(),
),
)
}
@@ -22,6 +22,8 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.search
import androidx.compose.runtime.Stable
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.CountFilter
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
@Stable
@@ -33,4 +35,12 @@ class SearchRelayListViewModel : BasicRelaySetupInfoModel() {
override suspend fun saveRelayList(urlList: List<NormalizedRelayUrl>) {
account.saveSearchRelayList(urlList)
}
override fun countFilters(relayUrl: NormalizedRelayUrl): List<CountFilter> =
listOf(
CountFilter(
label = "events",
filter = Filter(),
),
)
}