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:
+7
-87
@@ -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 = {
|
||||
|
||||
+103
-1
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
-173
@@ -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,
|
||||
|
||||
+16
@@ -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)),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
+14
@@ -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)),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
+10
@@ -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)),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
+91
-1
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
+10
@@ -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(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
+10
@@ -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(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user