- Only updates lists if they were changed.

- Refactoring RelayLists
This commit is contained in:
Vitor Pamplona
2024-05-30 17:10:38 -04:00
parent d9b96d2fd5
commit 7c65a93896
8 changed files with 197 additions and 363 deletions

View File

@@ -592,7 +592,7 @@ class Account(
return keyPair.privKey != null || signer is NostrSignerExternal return keyPair.privKey != null || signer is NostrSignerExternal
} }
fun sendNewRelayList(relays: Map<String, ContactListEvent.ReadWrite>) { fun sendKind3RelayList(relays: Map<String, ContactListEvent.ReadWrite>) {
if (!isWriteable()) return if (!isWriteable()) return
val contactList = userProfile().latestContactList val contactList = userProfile().latestContactList
@@ -2685,10 +2685,10 @@ class Account(
.toSet() .toSet()
} }
fun saveRelayList(value: List<RelaySetupInfo>) { fun saveKind3RelayList(value: List<RelaySetupInfo>) {
try { try {
localRelays = value.toSet() localRelays = value.toSet()
return sendNewRelayList( return sendKind3RelayList(
value.associate { it.url to ContactListEvent.ReadWrite(it.read, it.write) }, value.associate { it.url to ContactListEvent.ReadWrite(it.read, it.write) },
) )
} finally { } finally {

View File

@@ -0,0 +1,121 @@
/**
* Copyright (c) 2024 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.amethyst.ui.actions.relays
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.service.Nip11CachedRetriever
import com.vitorpamplona.amethyst.service.relays.RelayPool
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
abstract class BasicRelaySetupInfoModel : ViewModel() {
lateinit var account: Account
private val _relays = MutableStateFlow<List<BasicRelaySetupInfo>>(emptyList())
val relays = _relays.asStateFlow()
var hasModified = false
fun load(account: Account) {
this.account = account
clear()
loadRelayDocuments()
}
abstract fun getRelayList(): List<String>?
abstract fun saveRelayList(urlList: List<String>)
fun create() {
if (hasModified) {
viewModelScope.launch(Dispatchers.IO) {
saveRelayList(_relays.value.map { it.url })
clear()
}
}
}
fun loadRelayDocuments() {
viewModelScope.launch(Dispatchers.IO) {
_relays.value.forEach { item ->
Nip11CachedRetriever.loadRelayInfo(
dirtyUrl = item.url,
onInfo = {
togglePaidRelay(item, it.limitation?.payment_required ?: false)
},
onError = { url, errorCode, exceptionMessage -> },
)
}
}
}
fun clear() {
var hasModified = false
_relays.update {
val relayList = getRelayList() ?: emptyList()
relayList.map { relayUrl ->
val liveRelay = RelayPool.getRelay(relayUrl)
val errorCounter = liveRelay?.errorCounter ?: 0
val eventDownloadCounter = liveRelay?.eventDownloadCounterInBytes ?: 0
val eventUploadCounter = liveRelay?.eventUploadCounterInBytes ?: 0
val spamCounter = liveRelay?.spamCounter ?: 0
BasicRelaySetupInfo(
relayUrl,
errorCounter,
eventDownloadCounter,
eventUploadCounter,
spamCounter,
)
}.distinctBy { it.url }.sortedBy { it.downloadCountInBytes }.reversed()
}
}
fun addRelay(relay: BasicRelaySetupInfo) {
if (relays.value.any { it.url == relay.url }) return
_relays.update { it.plus(relay) }
hasModified = true
}
fun deleteRelay(relay: BasicRelaySetupInfo) {
_relays.update { it.minus(relay) }
hasModified = true
}
fun deleteAll() {
_relays.update { relays -> emptyList() }
hasModified = true
}
fun togglePaidRelay(
relay: BasicRelaySetupInfo,
paid: Boolean,
) {
_relays.update { it.updated(relay, relay.copy(paidRelay = paid)) }
}
}

View File

@@ -20,90 +20,12 @@
*/ */
package com.vitorpamplona.amethyst.ui.actions.relays package com.vitorpamplona.amethyst.ui.actions.relays
import androidx.lifecycle.ViewModel class DMRelayListViewModel : BasicRelaySetupInfoModel() {
import androidx.lifecycle.viewModelScope override fun getRelayList(): List<String>? {
import com.vitorpamplona.amethyst.model.Account return account.getDMRelayList()?.relays()
import com.vitorpamplona.amethyst.service.Nip11CachedRetriever
import com.vitorpamplona.amethyst.service.relays.RelayPool
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
class DMRelayListViewModel : ViewModel() {
private lateinit var account: Account
private val _relays = MutableStateFlow<List<BasicRelaySetupInfo>>(emptyList())
val relays = _relays.asStateFlow()
fun load(account: Account) {
this.account = account
clear()
loadRelayDocuments()
} }
fun create() { override fun saveRelayList(urlList: List<String>) {
viewModelScope.launch(Dispatchers.IO) { account.saveDMRelayList(urlList)
account.saveDMRelayList(_relays.value.map { it.url })
clear()
}
}
fun loadRelayDocuments() {
viewModelScope.launch(Dispatchers.IO) {
_relays.value.forEach { item ->
Nip11CachedRetriever.loadRelayInfo(
dirtyUrl = item.url,
onInfo = {
togglePaidRelay(item, it.limitation?.payment_required ?: false)
},
onError = { url, errorCode, exceptionMessage -> },
)
}
}
}
fun clear() {
_relays.update {
val relayList = account.getDMRelayList()?.relays() ?: emptyList()
relayList.map { relayUrl ->
val liveRelay = RelayPool.getRelay(relayUrl)
val errorCounter = liveRelay?.errorCounter ?: 0
val eventDownloadCounter = liveRelay?.eventDownloadCounterInBytes ?: 0
val eventUploadCounter = liveRelay?.eventUploadCounterInBytes ?: 0
val spamCounter = liveRelay?.spamCounter ?: 0
BasicRelaySetupInfo(
relayUrl,
errorCounter,
eventDownloadCounter,
eventUploadCounter,
spamCounter,
)
}.distinctBy { it.url }.sortedBy { it.downloadCountInBytes }.reversed()
}
}
fun addRelay(relay: BasicRelaySetupInfo) {
if (relays.value.any { it.url == relay.url }) return
_relays.update { it.plus(relay) }
}
fun deleteRelay(relay: BasicRelaySetupInfo) {
_relays.update { it.minus(relay) }
}
fun deleteAll() {
_relays.update { relays -> emptyList() }
}
fun togglePaidRelay(
relay: BasicRelaySetupInfo,
paid: Boolean,
) {
_relays.update { it.updated(relay, relay.copy(paidRelay = paid)) }
} }
} }

View File

@@ -41,6 +41,8 @@ class Kind3RelayListViewModel : ViewModel() {
private val _relays = MutableStateFlow<List<RelaySetupInfo>>(emptyList()) private val _relays = MutableStateFlow<List<RelaySetupInfo>>(emptyList())
val relays = _relays.asStateFlow() val relays = _relays.asStateFlow()
var hasModified = false
fun load(account: Account) { fun load(account: Account) {
this.account = account this.account = account
clear() clear()
@@ -48,9 +50,9 @@ class Kind3RelayListViewModel : ViewModel() {
} }
fun create() { fun create() {
relays.let { if (hasModified) {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
account.saveRelayList(it.value) account.saveKind3RelayList(relays.value)
clear() clear()
} }
} }
@@ -71,6 +73,7 @@ class Kind3RelayListViewModel : ViewModel() {
} }
fun clear() { fun clear() {
hasModified = false
_relays.update { _relays.update {
var relayFile = account.userProfile().latestContactList?.relays() var relayFile = account.userProfile().latestContactList?.relays()
@@ -140,47 +143,58 @@ class Kind3RelayListViewModel : ViewModel() {
if (relays.value.any { it.url == relay.url }) return if (relays.value.any { it.url == relay.url }) return
_relays.update { it.plus(relay) } _relays.update { it.plus(relay) }
hasModified = true
} }
fun deleteRelay(relay: RelaySetupInfo) { fun deleteRelay(relay: RelaySetupInfo) {
_relays.update { it.minus(relay) } _relays.update { it.minus(relay) }
hasModified = true
} }
fun deleteAll() { fun deleteAll() {
_relays.update { relays -> emptyList() } _relays.update { relays -> emptyList() }
hasModified = true
} }
fun toggleDownload(relay: RelaySetupInfo) { fun toggleDownload(relay: RelaySetupInfo) {
_relays.update { it.updated(relay, relay.copy(read = !relay.read)) } _relays.update { it.updated(relay, relay.copy(read = !relay.read)) }
hasModified = true
} }
fun toggleUpload(relay: RelaySetupInfo) { fun toggleUpload(relay: RelaySetupInfo) {
_relays.update { it.updated(relay, relay.copy(write = !relay.write)) } _relays.update { it.updated(relay, relay.copy(write = !relay.write)) }
hasModified = true
} }
fun toggleFollows(relay: RelaySetupInfo) { fun toggleFollows(relay: RelaySetupInfo) {
val newTypes = togglePresenceInSet(relay.feedTypes, FeedType.FOLLOWS) val newTypes = togglePresenceInSet(relay.feedTypes, FeedType.FOLLOWS)
_relays.update { it.updated(relay, relay.copy(feedTypes = newTypes)) } _relays.update { it.updated(relay, relay.copy(feedTypes = newTypes)) }
hasModified = true
} }
fun toggleMessages(relay: RelaySetupInfo) { fun toggleMessages(relay: RelaySetupInfo) {
val newTypes = togglePresenceInSet(relay.feedTypes, FeedType.PRIVATE_DMS) val newTypes = togglePresenceInSet(relay.feedTypes, FeedType.PRIVATE_DMS)
_relays.update { it.updated(relay, relay.copy(feedTypes = newTypes)) } _relays.update { it.updated(relay, relay.copy(feedTypes = newTypes)) }
hasModified = true
} }
fun togglePublicChats(relay: RelaySetupInfo) { fun togglePublicChats(relay: RelaySetupInfo) {
val newTypes = togglePresenceInSet(relay.feedTypes, FeedType.PUBLIC_CHATS) val newTypes = togglePresenceInSet(relay.feedTypes, FeedType.PUBLIC_CHATS)
_relays.update { it.updated(relay, relay.copy(feedTypes = newTypes)) } _relays.update { it.updated(relay, relay.copy(feedTypes = newTypes)) }
hasModified = true
} }
fun toggleGlobal(relay: RelaySetupInfo) { fun toggleGlobal(relay: RelaySetupInfo) {
val newTypes = togglePresenceInSet(relay.feedTypes, FeedType.GLOBAL) val newTypes = togglePresenceInSet(relay.feedTypes, FeedType.GLOBAL)
_relays.update { it.updated(relay, relay.copy(feedTypes = newTypes)) } _relays.update { it.updated(relay, relay.copy(feedTypes = newTypes)) }
hasModified = true
} }
fun toggleSearch(relay: RelaySetupInfo) { fun toggleSearch(relay: RelaySetupInfo) {
val newTypes = togglePresenceInSet(relay.feedTypes, FeedType.SEARCH) val newTypes = togglePresenceInSet(relay.feedTypes, FeedType.SEARCH)
_relays.update { it.updated(relay, relay.copy(feedTypes = newTypes)) } _relays.update { it.updated(relay, relay.copy(feedTypes = newTypes)) }
hasModified = true
} }
fun togglePaidRelay( fun togglePaidRelay(

View File

@@ -20,90 +20,12 @@
*/ */
package com.vitorpamplona.amethyst.ui.actions.relays package com.vitorpamplona.amethyst.ui.actions.relays
import androidx.lifecycle.ViewModel class LocalRelayListViewModel : BasicRelaySetupInfoModel() {
import androidx.lifecycle.viewModelScope override fun getRelayList(): List<String>? {
import com.vitorpamplona.amethyst.model.Account return account.localRelayServers.toList()
import com.vitorpamplona.amethyst.service.Nip11CachedRetriever
import com.vitorpamplona.amethyst.service.relays.RelayPool
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
class LocalRelayListViewModel : ViewModel() {
private lateinit var account: Account
private val _relays = MutableStateFlow<List<BasicRelaySetupInfo>>(emptyList())
val relays = _relays.asStateFlow()
fun load(account: Account) {
this.account = account
clear()
loadRelayDocuments()
} }
fun create() { override fun saveRelayList(urlList: List<String>) {
viewModelScope.launch(Dispatchers.IO) { account.updateLocalRelayServers(urlList.toSet())
account.updateLocalRelayServers(_relays.value.map { it.url }.toSet())
clear()
}
}
fun loadRelayDocuments() {
viewModelScope.launch(Dispatchers.IO) {
_relays.value.forEach { item ->
Nip11CachedRetriever.loadRelayInfo(
dirtyUrl = item.url,
onInfo = {
togglePaidRelay(item, it.limitation?.payment_required ?: false)
},
onError = { url, errorCode, exceptionMessage -> },
)
}
}
}
fun clear() {
_relays.update {
val relayList = account.localRelayServers ?: emptyList()
relayList.map { relayUrl ->
val liveRelay = RelayPool.getRelay(relayUrl)
val errorCounter = liveRelay?.errorCounter ?: 0
val eventDownloadCounter = liveRelay?.eventDownloadCounterInBytes ?: 0
val eventUploadCounter = liveRelay?.eventUploadCounterInBytes ?: 0
val spamCounter = liveRelay?.spamCounter ?: 0
BasicRelaySetupInfo(
relayUrl,
errorCounter,
eventDownloadCounter,
eventUploadCounter,
spamCounter,
)
}.distinctBy { it.url }.sortedBy { it.downloadCountInBytes }.reversed()
}
}
fun addRelay(relay: BasicRelaySetupInfo) {
if (relays.value.any { it.url == relay.url }) return
_relays.update { it.plus(relay) }
}
fun deleteRelay(relay: BasicRelaySetupInfo) {
_relays.update { it.minus(relay) }
}
fun deleteAll() {
_relays.update { relays -> emptyList() }
}
fun togglePaidRelay(
relay: BasicRelaySetupInfo,
paid: Boolean,
) {
_relays.update { it.updated(relay, relay.copy(paidRelay = paid)) }
} }
} }

View File

@@ -41,6 +41,8 @@ class Nip65RelayListViewModel : ViewModel() {
private val _notificationRelays = MutableStateFlow<List<BasicRelaySetupInfo>>(emptyList()) private val _notificationRelays = MutableStateFlow<List<BasicRelaySetupInfo>>(emptyList())
val notificationRelays = _notificationRelays.asStateFlow() val notificationRelays = _notificationRelays.asStateFlow()
var hasModified = false
fun load(account: Account) { fun load(account: Account) {
this.account = account this.account = account
clear() clear()
@@ -48,26 +50,28 @@ class Nip65RelayListViewModel : ViewModel() {
} }
fun create() { fun create() {
viewModelScope.launch(Dispatchers.IO) { if (hasModified) {
val writes = _homeRelays.value.map { it.url }.toSet() viewModelScope.launch(Dispatchers.IO) {
val reads = _notificationRelays.value.map { it.url }.toSet() val writes = _homeRelays.value.map { it.url }.toSet()
val reads = _notificationRelays.value.map { it.url }.toSet()
val urls = writes.union(reads) val urls = writes.union(reads)
account.sendNip65RelayList( account.sendNip65RelayList(
urls.map { urls.map {
val type = val type =
if (writes.contains(it) && reads.contains(it)) { if (writes.contains(it) && reads.contains(it)) {
AdvertisedRelayListEvent.AdvertisedRelayType.BOTH AdvertisedRelayListEvent.AdvertisedRelayType.BOTH
} else if (writes.contains(it)) { } else if (writes.contains(it)) {
AdvertisedRelayListEvent.AdvertisedRelayType.WRITE AdvertisedRelayListEvent.AdvertisedRelayType.WRITE
} else { } else {
AdvertisedRelayListEvent.AdvertisedRelayType.READ AdvertisedRelayListEvent.AdvertisedRelayType.READ
} }
AdvertisedRelayListEvent.AdvertisedRelayInfo(it, type) AdvertisedRelayListEvent.AdvertisedRelayInfo(it, type)
}, },
) )
clear() clear()
}
} }
} }
@@ -96,18 +100,19 @@ class Nip65RelayListViewModel : ViewModel() {
} }
fun clear() { fun clear() {
hasModified = false
_homeRelays.update { _homeRelays.update {
val relayList = account.getNIP65RelayList()?.relays() ?: emptyList() val relayList = account.getNIP65RelayList()?.writeRelays() ?: emptyList()
relayList.filter { it.type == AdvertisedRelayListEvent.AdvertisedRelayType.BOTH || it.type == AdvertisedRelayListEvent.AdvertisedRelayType.WRITE }.map { relayUrl -> relayList.map { relayUrl ->
val liveRelay = RelayPool.getRelay(relayUrl.relayUrl) val liveRelay = RelayPool.getRelay(relayUrl)
val errorCounter = liveRelay?.errorCounter ?: 0 val errorCounter = liveRelay?.errorCounter ?: 0
val eventDownloadCounter = liveRelay?.eventDownloadCounterInBytes ?: 0 val eventDownloadCounter = liveRelay?.eventDownloadCounterInBytes ?: 0
val eventUploadCounter = liveRelay?.eventUploadCounterInBytes ?: 0 val eventUploadCounter = liveRelay?.eventUploadCounterInBytes ?: 0
val spamCounter = liveRelay?.spamCounter ?: 0 val spamCounter = liveRelay?.spamCounter ?: 0
BasicRelaySetupInfo( BasicRelaySetupInfo(
relayUrl.relayUrl, relayUrl,
errorCounter, errorCounter,
eventDownloadCounter, eventDownloadCounter,
eventUploadCounter, eventUploadCounter,
@@ -117,17 +122,17 @@ class Nip65RelayListViewModel : ViewModel() {
} }
_notificationRelays.update { _notificationRelays.update {
val relayList = account.getNIP65RelayList()?.relays() ?: emptyList() val relayList = account.getNIP65RelayList()?.readRelays() ?: emptyList()
relayList.filter { it.type == AdvertisedRelayListEvent.AdvertisedRelayType.BOTH || it.type == AdvertisedRelayListEvent.AdvertisedRelayType.READ }.map { relayUrl -> relayList.map { relayUrl ->
val liveRelay = RelayPool.getRelay(relayUrl.relayUrl) val liveRelay = RelayPool.getRelay(relayUrl)
val errorCounter = liveRelay?.errorCounter ?: 0 val errorCounter = liveRelay?.errorCounter ?: 0
val eventDownloadCounter = liveRelay?.eventDownloadCounterInBytes ?: 0 val eventDownloadCounter = liveRelay?.eventDownloadCounterInBytes ?: 0
val eventUploadCounter = liveRelay?.eventUploadCounterInBytes ?: 0 val eventUploadCounter = liveRelay?.eventUploadCounterInBytes ?: 0
val spamCounter = liveRelay?.spamCounter ?: 0 val spamCounter = liveRelay?.spamCounter ?: 0
BasicRelaySetupInfo( BasicRelaySetupInfo(
relayUrl.relayUrl, relayUrl,
errorCounter, errorCounter,
eventDownloadCounter, eventDownloadCounter,
eventUploadCounter, eventUploadCounter,
@@ -141,14 +146,17 @@ class Nip65RelayListViewModel : ViewModel() {
if (_homeRelays.value.any { it.url == relay.url }) return if (_homeRelays.value.any { it.url == relay.url }) return
_homeRelays.update { it.plus(relay) } _homeRelays.update { it.plus(relay) }
hasModified = true
} }
fun deleteHomeRelay(relay: BasicRelaySetupInfo) { fun deleteHomeRelay(relay: BasicRelaySetupInfo) {
_homeRelays.update { it.minus(relay) } _homeRelays.update { it.minus(relay) }
hasModified = true
} }
fun deleteHomeAll() { fun deleteHomeAll() {
_homeRelays.update { relays -> emptyList() } _homeRelays.update { relays -> emptyList() }
hasModified = true
} }
fun toggleHomePaidRelay( fun toggleHomePaidRelay(
@@ -162,14 +170,17 @@ class Nip65RelayListViewModel : ViewModel() {
if (_notificationRelays.value.any { it.url == relay.url }) return if (_notificationRelays.value.any { it.url == relay.url }) return
_notificationRelays.update { it.plus(relay) } _notificationRelays.update { it.plus(relay) }
hasModified = true
} }
fun deleteNotifRelay(relay: BasicRelaySetupInfo) { fun deleteNotifRelay(relay: BasicRelaySetupInfo) {
_notificationRelays.update { it.minus(relay) } _notificationRelays.update { it.minus(relay) }
hasModified = true
} }
fun deleteNotifAll() { fun deleteNotifAll() {
_notificationRelays.update { relays -> emptyList() } _notificationRelays.update { relays -> emptyList() }
hasModified = true
} }
fun toggleNotifPaidRelay( fun toggleNotifPaidRelay(

View File

@@ -20,90 +20,12 @@
*/ */
package com.vitorpamplona.amethyst.ui.actions.relays package com.vitorpamplona.amethyst.ui.actions.relays
import androidx.lifecycle.ViewModel class PrivateOutboxRelayListViewModel : BasicRelaySetupInfoModel() {
import androidx.lifecycle.viewModelScope override fun getRelayList(): List<String>? {
import com.vitorpamplona.amethyst.model.Account return account.getPrivateOutboxRelayList()?.relays()
import com.vitorpamplona.amethyst.service.Nip11CachedRetriever
import com.vitorpamplona.amethyst.service.relays.RelayPool
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
class PrivateOutboxRelayListViewModel : ViewModel() {
private lateinit var account: Account
private val _relays = MutableStateFlow<List<BasicRelaySetupInfo>>(emptyList())
val relays = _relays.asStateFlow()
fun load(account: Account) {
this.account = account
clear()
loadRelayDocuments()
} }
fun create() { override fun saveRelayList(urlList: List<String>) {
viewModelScope.launch(Dispatchers.IO) { account.savePrivateOutboxRelayList(urlList)
account.savePrivateOutboxRelayList(_relays.value.map { it.url })
clear()
}
}
fun loadRelayDocuments() {
viewModelScope.launch(Dispatchers.IO) {
_relays.value.forEach { item ->
Nip11CachedRetriever.loadRelayInfo(
dirtyUrl = item.url,
onInfo = {
togglePaidRelay(item, it.limitation?.payment_required ?: false)
},
onError = { url, errorCode, exceptionMessage -> },
)
}
}
}
fun clear() {
_relays.update {
val relayList = account.getPrivateOutboxRelayList()?.relays() ?: emptyList()
relayList.map { relayUrl ->
val liveRelay = RelayPool.getRelay(relayUrl)
val errorCounter = liveRelay?.errorCounter ?: 0
val eventDownloadCounter = liveRelay?.eventDownloadCounterInBytes ?: 0
val eventUploadCounter = liveRelay?.eventUploadCounterInBytes ?: 0
val spamCounter = liveRelay?.spamCounter ?: 0
BasicRelaySetupInfo(
relayUrl,
errorCounter,
eventDownloadCounter,
eventUploadCounter,
spamCounter,
)
}.distinctBy { it.url }.sortedBy { it.downloadCountInBytes }.reversed()
}
}
fun addRelay(relay: BasicRelaySetupInfo) {
if (relays.value.any { it.url == relay.url }) return
_relays.update { it.plus(relay) }
}
fun deleteRelay(relay: BasicRelaySetupInfo) {
_relays.update { it.minus(relay) }
}
fun deleteAll() {
_relays.update { relays -> emptyList() }
}
fun togglePaidRelay(
relay: BasicRelaySetupInfo,
paid: Boolean,
) {
_relays.update { it.updated(relay, relay.copy(paidRelay = paid)) }
} }
} }

View File

@@ -20,90 +20,12 @@
*/ */
package com.vitorpamplona.amethyst.ui.actions.relays package com.vitorpamplona.amethyst.ui.actions.relays
import androidx.lifecycle.ViewModel class SearchRelayListViewModel : BasicRelaySetupInfoModel() {
import androidx.lifecycle.viewModelScope override fun getRelayList(): List<String>? {
import com.vitorpamplona.amethyst.model.Account return account.getSearchRelayList()?.relays()
import com.vitorpamplona.amethyst.service.Nip11CachedRetriever
import com.vitorpamplona.amethyst.service.relays.RelayPool
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
class SearchRelayListViewModel : ViewModel() {
private lateinit var account: Account
private val _relays = MutableStateFlow<List<BasicRelaySetupInfo>>(emptyList())
val relays = _relays.asStateFlow()
fun load(account: Account) {
this.account = account
clear()
loadRelayDocuments()
} }
fun create() { override fun saveRelayList(urlList: List<String>) {
viewModelScope.launch(Dispatchers.IO) { account.saveSearchRelayList(urlList)
account.saveSearchRelayList(_relays.value.map { it.url })
clear()
}
}
fun loadRelayDocuments() {
viewModelScope.launch(Dispatchers.IO) {
_relays.value.forEach { item ->
Nip11CachedRetriever.loadRelayInfo(
dirtyUrl = item.url,
onInfo = {
togglePaidRelay(item, it.limitation?.payment_required ?: false)
},
onError = { url, errorCode, exceptionMessage -> },
)
}
}
}
fun clear() {
_relays.update {
val relayList = account.getSearchRelayList()?.relays() ?: emptyList()
relayList.map { relayUrl ->
val liveRelay = RelayPool.getRelay(relayUrl)
val errorCounter = liveRelay?.errorCounter ?: 0
val eventDownloadCounter = liveRelay?.eventDownloadCounterInBytes ?: 0
val eventUploadCounter = liveRelay?.eventUploadCounterInBytes ?: 0
val spamCounter = liveRelay?.spamCounter ?: 0
BasicRelaySetupInfo(
relayUrl,
errorCounter,
eventDownloadCounter,
eventUploadCounter,
spamCounter,
)
}.distinctBy { it.url }.sortedBy { it.downloadCountInBytes }.reversed()
}
}
fun addRelay(relay: BasicRelaySetupInfo) {
if (relays.value.any { it.url == relay.url }) return
_relays.update { it.plus(relay) }
}
fun deleteRelay(relay: BasicRelaySetupInfo) {
_relays.update { it.minus(relay) }
}
fun deleteAll() {
_relays.update { relays -> emptyList() }
}
fun togglePaidRelay(
relay: BasicRelaySetupInfo,
paid: Boolean,
) {
_relays.update { it.updated(relay, relay.copy(paidRelay = paid)) }
} }
} }