Refactor relay handling to be per-account

This commit is contained in:
greenart7c3
2025-11-24 09:10:11 -03:00
parent 5b914da6fa
commit 2ea05a3d2a
10 changed files with 56 additions and 33 deletions
@@ -390,14 +390,12 @@ class Amber : Application(), LifecycleObserver {
return historyDatabases[npub]!!
}
fun getSavedRelays(): Set<NormalizedRelayUrl> {
fun getSavedRelays(account: Account): Set<NormalizedRelayUrl> {
val savedRelays = mutableSetOf<NormalizedRelayUrl>()
LocalPreferences.allSavedAccounts(this).forEach { accountInfo ->
val database = getDatabase(accountInfo.npub)
database.dao().getAllApplications().forEach {
it.application.relays.forEach { setupInfo ->
savedRelays.add(setupInfo)
}
val database = getDatabase(account.npub)
database.dao().getAllApplications().forEach {
it.application.relays.forEach { setupInfo ->
savedRelays.add(setupInfo)
}
}
@@ -142,7 +142,7 @@ object BunkerRequestUtils {
account = account,
localKey = bunkerRequest.localKey,
encryptedContent = encryptedContent,
relays = relays.ifEmpty { Amber.instance.getSavedRelays().toList() },
relays = relays.ifEmpty { Amber.instance.getSavedRelays(account).toList() },
onLoading = onLoading,
onDone = onDone,
)
@@ -341,7 +341,7 @@ object BunkerRequestUtils {
)
}
// check if application has any relay not in saved relays
val savedRelays = Amber.instance.getSavedRelays()
val savedRelays = Amber.instance.getSavedRelays(account)
if (relays.any { !savedRelays.contains(it) }) {
didChangeRelays = true
}
@@ -118,7 +118,7 @@ object NostrConnectUtils {
},
),
localKey = pubKey,
relays = relays.ifEmpty { Amber.instance.getSavedRelays().toList() },
relays = relays.ifEmpty { Amber.instance.getSavedRelays(account).toList() },
currentAccount = account.npub,
nostrConnectSecret = nostrConnectSecret,
closeApplication = intent.getBooleanExtra("closeApplication", true),
@@ -24,7 +24,7 @@ import android.content.Context
import android.util.Log
import com.greenart7c3.nostrsigner.Amber
import com.greenart7c3.nostrsigner.LocalPreferences
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
import com.greenart7c3.nostrsigner.models.Account
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.IRelayClientListener
import com.vitorpamplona.quartz.nip01Core.relay.client.single.IRelayClient
@@ -33,7 +33,6 @@ import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.Message
import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.Command
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip19Bech32.bech32.bechToBytes
import com.vitorpamplona.quartz.nip46RemoteSigner.NostrConnectEvent
import com.vitorpamplona.quartz.utils.TimeUtils
import java.util.UUID
@@ -43,7 +42,7 @@ class NotificationSubscription(
val appContext: Context,
) : IRelayClientListener {
private val eventNotificationConsumer = EventNotificationConsumer(appContext)
private val subId = UUID.randomUUID().toString()
private val subIds = mutableMapOf<String, String>()
init {
// listens until the app crashes.
@@ -52,7 +51,7 @@ class NotificationSubscription(
override fun onIncomingMessage(relay: IRelayClient, msgStr: String, msg: Message) {
if (msg is EventMessage) {
if (msg.subId == subId) {
if (subIds.containsValue(msg.subId)) {
eventNotificationConsumer.consume(msg.event, relay.url)
}
}
@@ -67,22 +66,26 @@ class NotificationSubscription(
/**
* Call this method every time the relay list or the user list changes
*/
fun updateFilter() {
client.openReqSubscription(subId, createNotificationsFilter())
suspend fun updateFilter() {
LocalPreferences.allAccounts(Amber.instance).forEach {
if (!subIds.containsKey(it.hexKey)) {
subIds[it.hexKey] = UUID.randomUUID().toString()
}
client.openReqSubscription(subIds[it.hexKey]!!, createNotificationsFilter(it))
}
}
private fun createNotificationsFilter(): Map<NormalizedRelayUrl, List<Filter>> {
// TODO: If you break relays per account, you can change this to only send the requests to the right relays for each account.
val relays = Amber.instance.getSavedRelays()
private fun createNotificationsFilter(account: Account): Map<NormalizedRelayUrl, List<Filter>> {
val relays = Amber.instance.getSavedRelays(account)
var since = TimeUtils.now()
val accounts = LocalPreferences.allSavedAccounts(appContext)
val latest = if (Amber.instance.notificationCache.size() > 0) Amber.instance.notificationCache.snapshot().maxOf { it.value } else 0L
if (latest > 0) {
since = latest
}
val pubKeys = accounts.map { it.npub.bechToBytes().toHexKey() }
val pubKeys = listOf(account.hexKey)
return relays.associateWith {
listOf(
@@ -24,8 +24,8 @@ import android.content.Context
import com.greenart7c3.nostrsigner.AccountInfo
import com.greenart7c3.nostrsigner.Amber
import com.greenart7c3.nostrsigner.LocalPreferences
import com.greenart7c3.nostrsigner.models.Account
import com.vitorpamplona.quartz.nip01Core.core.hexToByteArray
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
import com.vitorpamplona.quartz.nip01Core.relay.client.NostrClient
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.IRelayClientListener
@@ -35,7 +35,6 @@ import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.EventMessage
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.nip19Bech32.bech32.bechToBytes
import com.vitorpamplona.quartz.nip19Bech32.toNpub
import com.vitorpamplona.quartz.utils.TimeUtils
import java.util.UUID
@@ -48,7 +47,7 @@ class ProfileSubscription(
val appContext: Context,
val scope: CoroutineScope,
) : IRelayClientListener {
private val subId = UUID.randomUUID().toString()
private val subIds = mutableMapOf<String, String>()
private val monitoringAccounts: List<AccountInfo> = emptyList()
init {
@@ -63,7 +62,7 @@ class ProfileSubscription(
}
}
if (msg is EventMessage) {
if (this.subId == msg.subId) {
if (this.subIds.containsValue(msg.subId)) {
if (msg.event.kind == MetadataEvent.KIND) {
(msg.event as MetadataEvent).contactMetaData()?.let { metadata ->
val npub = msg.event.pubKey.hexToByteArray().toNpub()
@@ -101,21 +100,27 @@ class ProfileSubscription(
/**
* Call this method every time the relay list or the user list changes
*/
fun updateFilter() {
client.openReqSubscription(subId, createProfileFilter())
suspend fun updateFilter() {
LocalPreferences.allAccounts(Amber.instance).forEach {
if (!subIds.containsKey(it.hexKey)) {
subIds[it.hexKey] = UUID.randomUUID().toString()
}
client.openReqSubscription(subIds[it.hexKey]!!, createProfileFilter(it))
}
}
/**
* Call this function when you want to stop updates
*/
fun closeSub() {
client.close(subId)
subIds.values.forEach {
client.close(it)
}
}
private fun createProfileFilter(): Map<NormalizedRelayUrl, List<Filter>> {
private fun createProfileFilter(account: Account): Map<NormalizedRelayUrl, List<Filter>> {
val relays = LocalPreferences.loadSettingsFromEncryptedStorage().defaultProfileRelays
val monitoringAccounts = LocalPreferences.allSavedAccounts(appContext)
val accounts = monitoringAccounts.map { it.npub.bechToBytes().toHexKey() }
val accounts = listOf(account.hexKey)
return relays.associateWith {
listOf(
Filter(
@@ -28,6 +28,7 @@ import com.greenart7c3.nostrsigner.Amber
import com.greenart7c3.nostrsigner.BuildConfig
import com.greenart7c3.nostrsigner.LocalPreferences
import com.greenart7c3.nostrsigner.R
import com.greenart7c3.nostrsigner.models.Account
import com.greenart7c3.nostrsigner.ui.actions.onAddRelay
import com.greenart7c3.nostrsigner.ui.components.AmberButton
import kotlinx.coroutines.Dispatchers
@@ -37,6 +38,7 @@ import kotlinx.coroutines.launch
fun DefaultProfileRelaysScreen(
modifier: Modifier,
accountStateViewModel: AccountStateViewModel,
account: Account,
) {
val scope = rememberCoroutineScope()
val context = LocalContext.current
@@ -93,6 +95,7 @@ fun DefaultProfileRelaysScreen(
relays2,
scope,
accountStateViewModel,
account,
context,
shouldCheckForBunker = false,
onDone = {
@@ -130,6 +133,7 @@ fun DefaultProfileRelaysScreen(
relays2,
scope,
accountStateViewModel,
account,
context,
shouldCheckForBunker = false,
onDone = {
@@ -167,6 +167,7 @@ fun EditConfigurationScreen(
relays,
scope,
accountStateViewModel,
account,
context,
onDone = {},
)
@@ -189,6 +190,7 @@ fun EditConfigurationScreen(
relays,
scope,
accountStateViewModel,
account,
context,
onDone = {},
)
@@ -502,6 +502,7 @@ fun MainScreen(
val scrollState = rememberScrollState()
ActiveRelaysScreen(
navController = navController,
account = account,
modifier =
Modifier
.fillMaxSize()
@@ -532,6 +533,7 @@ fun MainScreen(
Route.DefaultRelays.route,
content = {
DefaultRelaysScreen(
account = account,
modifier =
Modifier
.fillMaxSize()
@@ -807,6 +809,7 @@ fun MainScreen(
.padding(horizontal = verticalPadding)
.padding(top = verticalPadding * 1.5f),
accountStateViewModel = accountStateViewModel,
account = account,
)
},
)
@@ -164,6 +164,7 @@ fun NewNsecBunkerScreen(
relays,
scope,
accountStateViewModel,
account = account,
context,
onDone = {
},
@@ -187,6 +188,7 @@ fun NewNsecBunkerScreen(
relays,
scope,
accountStateViewModel,
account = account,
context,
onDone = {
},
@@ -48,6 +48,7 @@ import com.greenart7c3.nostrsigner.BuildConfig
import com.greenart7c3.nostrsigner.LocalPreferences
import com.greenart7c3.nostrsigner.R
import com.greenart7c3.nostrsigner.checkNotInMainThread
import com.greenart7c3.nostrsigner.models.Account
import com.greenart7c3.nostrsigner.models.TimeUtils.formatLongToCustomDateTimeWithSeconds
import com.greenart7c3.nostrsigner.models.defaultAppRelays
import com.greenart7c3.nostrsigner.okhttp.HttpClientManager
@@ -86,6 +87,7 @@ import kotlinx.coroutines.withContext
fun DefaultRelaysScreen(
modifier: Modifier,
accountStateViewModel: AccountStateViewModel,
account: Account,
) {
val scope = rememberCoroutineScope()
val context = LocalContext.current
@@ -162,6 +164,7 @@ fun DefaultRelaysScreen(
relays2,
scope,
accountStateViewModel,
account,
context,
onDone = {
Amber.instance.settings = Amber.instance.settings.copy(
@@ -198,6 +201,7 @@ fun DefaultRelaysScreen(
relays2,
scope,
accountStateViewModel,
account,
context,
onDone = {
isLoading.value = true
@@ -259,6 +263,7 @@ fun onAddRelay(
relays2: SnapshotStateList<NormalizedRelayUrl>,
scope: CoroutineScope,
accountStateViewModel: AccountStateViewModel,
account: Account,
context: Context,
shouldCheckForBunker: Boolean = true,
onDone: () -> Unit,
@@ -295,7 +300,7 @@ fun onAddRelay(
onInfo = { info ->
scope.launch(Dispatchers.IO) secondLaunch@{
if (shouldCheckForBunker) {
val relays = Amber.instance.getSavedRelays()
val relays = Amber.instance.getSavedRelays(account)
if (addedWSS in relays) {
relays2.add(addedWSS)
onDone()
@@ -507,6 +512,7 @@ fun RelayLogScreen(
fun ActiveRelaysScreen(
modifier: Modifier,
navController: NavController,
account: Account,
) {
val relays2 =
remember {
@@ -515,7 +521,7 @@ fun ActiveRelaysScreen(
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
relays2.addAll(Amber.instance.getSavedRelays())
relays2.addAll(Amber.instance.getSavedRelays(account))
}
}