feat(cache): extract Kind3FollowListState + Nip65RelayListState to commons
- Add Kind3FollowListState to commons/commonMain with ICacheProvider + Kind3FollowListRepository interface for settings needs - Add Nip65RelayListState to commons/commonMain with ICacheProvider + Nip65RelayListRepository interface with default relay sets - Per-feature repository interfaces match existing EphemeralChatRepository / PublicChatListRepository pattern in commons - Android originals unchanged — Desktop will use commons versions - Static LocalCache.justConsumeMyOwnEvent calls replaced with cache param
This commit is contained in:
+33
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 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.commons.model.nip02FollowList
|
||||||
|
|
||||||
|
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Narrow repository interface for Kind3FollowListState's settings needs.
|
||||||
|
* Follows the established pattern of EphemeralChatRepository / PublicChatListRepository.
|
||||||
|
*/
|
||||||
|
interface Kind3FollowListRepository {
|
||||||
|
val backupContactList: ContactListEvent?
|
||||||
|
|
||||||
|
fun updateContactListTo(event: ContactListEvent)
|
||||||
|
}
|
||||||
+181
@@ -0,0 +1,181 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 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.commons.model.nip02FollowList
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import com.vitorpamplona.amethyst.commons.model.NoteState
|
||||||
|
import com.vitorpamplona.amethyst.commons.model.User
|
||||||
|
import com.vitorpamplona.amethyst.commons.model.cache.ICacheProvider
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||||
|
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
|
||||||
|
import com.vitorpamplona.quartz.nip02FollowList.tags.ContactTag
|
||||||
|
import com.vitorpamplona.quartz.utils.Log
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import kotlinx.coroutines.flow.transformLatest
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class Kind3FollowListState(
|
||||||
|
val signer: NostrSigner,
|
||||||
|
val cache: ICacheProvider,
|
||||||
|
val scope: CoroutineScope,
|
||||||
|
val settings: Kind3FollowListRepository,
|
||||||
|
) {
|
||||||
|
// Creates a long-term reference for this note so that the GC doesn't collect the note itself
|
||||||
|
val user = cache.getOrCreateUser(signer.pubKey)
|
||||||
|
|
||||||
|
// Creates a long-term reference for this note so that the GC doesn't collect the note itself
|
||||||
|
val note = cache.getOrCreateAddressableNote(getFollowListAddress())
|
||||||
|
|
||||||
|
fun getFollowListAddress() = ContactListEvent.createAddress(signer.pubKey)
|
||||||
|
|
||||||
|
fun getFollowListFlow(): StateFlow<NoteState> = note.flow().metadata.stateFlow
|
||||||
|
|
||||||
|
fun getFollowListEvent(): ContactListEvent? = note.event as? ContactListEvent
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
private val innerFlow: Flow<Kind3Follows> =
|
||||||
|
getFollowListFlow().transformLatest {
|
||||||
|
emit(buildKind3Follows(it.note.event as? ContactListEvent ?: settings.backupContactList))
|
||||||
|
}
|
||||||
|
|
||||||
|
val flow =
|
||||||
|
innerFlow
|
||||||
|
.flowOn(Dispatchers.IO)
|
||||||
|
.stateIn(
|
||||||
|
scope,
|
||||||
|
SharingStarted.Eagerly,
|
||||||
|
// this has priority.
|
||||||
|
buildKind3Follows(getFollowListEvent() ?: settings.backupContactList),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Creates a long-term reference for all follows of a user
|
||||||
|
val userList =
|
||||||
|
flow
|
||||||
|
.map { kind3Follows ->
|
||||||
|
kind3Follows.authors.mapNotNull {
|
||||||
|
runCatching { cache.getOrCreateUser(it) }.getOrNull()
|
||||||
|
}
|
||||||
|
}.flowOn(Dispatchers.IO)
|
||||||
|
.stateIn(
|
||||||
|
scope,
|
||||||
|
SharingStarted.Eagerly,
|
||||||
|
// this has priority.
|
||||||
|
flow.value.authors.mapNotNull {
|
||||||
|
runCatching { cache.getOrCreateUser(it) }.getOrNull()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
This contains a big OR of everything the user wants to see in the a single feed.
|
||||||
|
*/
|
||||||
|
@Immutable
|
||||||
|
class Kind3Follows(
|
||||||
|
val authors: Set<HexKey> = emptySet(),
|
||||||
|
val authorsPlusMe: Set<HexKey>,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun buildKind3Follows(latestContactList: ContactListEvent?): Kind3Follows {
|
||||||
|
// makes sure the output include only valid p tags
|
||||||
|
val verifiedFollowingUsers = latestContactList?.verifiedFollowKeySet() ?: emptySet()
|
||||||
|
|
||||||
|
return Kind3Follows(
|
||||||
|
authors = verifiedFollowingUsers,
|
||||||
|
authorsPlusMe = verifiedFollowingUsers + signer.pubKey,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun follow(users: List<User>): ContactListEvent {
|
||||||
|
val contactList = getFollowListEvent()
|
||||||
|
|
||||||
|
val contacts =
|
||||||
|
users.map {
|
||||||
|
ContactTag(it.pubkeyHex, it.bestRelayHint(), null)
|
||||||
|
}
|
||||||
|
|
||||||
|
return if (contactList != null) {
|
||||||
|
ContactListEvent.followUsers(contactList, contacts, signer)
|
||||||
|
} else {
|
||||||
|
ContactListEvent.createFromScratch(
|
||||||
|
followUsers = contacts,
|
||||||
|
relayUse = emptyMap(),
|
||||||
|
signer = signer,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun follow(user: User): ContactListEvent {
|
||||||
|
val contactList = getFollowListEvent()
|
||||||
|
|
||||||
|
return if (contactList != null) {
|
||||||
|
ContactListEvent.followUser(contactList, user.pubkeyHex, signer)
|
||||||
|
} else {
|
||||||
|
ContactListEvent.createFromScratch(
|
||||||
|
followUsers = listOf(ContactTag(user.pubkeyHex, user.bestRelayHint(), null)),
|
||||||
|
relayUse = emptyMap(),
|
||||||
|
signer = signer,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun unfollow(user: User): ContactListEvent? {
|
||||||
|
val contactList = getFollowListEvent()
|
||||||
|
|
||||||
|
return if (contactList != null && contactList.tags.isNotEmpty()) {
|
||||||
|
ContactListEvent.unfollowUser(
|
||||||
|
contactList,
|
||||||
|
user.pubkeyHex,
|
||||||
|
signer,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
settings.backupContactList?.let {
|
||||||
|
Log.d("AccountRegisterObservers", "Loading saved ${it.tags.size} contacts")
|
||||||
|
|
||||||
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
|
scope.launch(Dispatchers.IO) { cache.justConsumeMyOwnEvent(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// saves contact list for the next time.
|
||||||
|
scope.launch(Dispatchers.IO) {
|
||||||
|
Log.d("AccountRegisterObservers", "Kind 3 Collector Start")
|
||||||
|
getFollowListFlow().collect {
|
||||||
|
Log.d("AccountRegisterObservers", "Updating Kind 3 ${signer.pubKey}")
|
||||||
|
(it.note.event as? ContactListEvent)?.let {
|
||||||
|
settings.updateContactListTo(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 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.commons.model.nip65RelayList
|
||||||
|
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||||
|
import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Narrow repository interface for Nip65RelayListState's settings needs.
|
||||||
|
* Follows the established pattern of EphemeralChatRepository / PublicChatListRepository.
|
||||||
|
*/
|
||||||
|
interface Nip65RelayListRepository {
|
||||||
|
val backupNIP65RelayList: AdvertisedRelayListEvent?
|
||||||
|
|
||||||
|
fun updateNIP65RelayList(event: AdvertisedRelayListEvent)
|
||||||
|
|
||||||
|
/** Default relay set when no NIP-65 list is available (write relays). */
|
||||||
|
val defaultOutboxRelays: Set<NormalizedRelayUrl>
|
||||||
|
|
||||||
|
/** Default relay set when no NIP-65 list is available (read relays). */
|
||||||
|
val defaultInboxRelays: Set<NormalizedRelayUrl>
|
||||||
|
}
|
||||||
+160
@@ -0,0 +1,160 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 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.commons.model.nip65RelayList
|
||||||
|
|
||||||
|
import com.vitorpamplona.amethyst.commons.model.Note
|
||||||
|
import com.vitorpamplona.amethyst.commons.model.NoteState
|
||||||
|
import com.vitorpamplona.amethyst.commons.model.cache.ICacheProvider
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||||
|
import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
|
||||||
|
import com.vitorpamplona.quartz.nip65RelayList.tags.AdvertisedRelayInfo
|
||||||
|
import com.vitorpamplona.quartz.utils.Log
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.onStart
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class Nip65RelayListState(
|
||||||
|
val signer: NostrSigner,
|
||||||
|
val cache: ICacheProvider,
|
||||||
|
val scope: CoroutineScope,
|
||||||
|
val settings: Nip65RelayListRepository,
|
||||||
|
) {
|
||||||
|
// Creates a long-term reference for this note so that the GC doesn't collect the note itself
|
||||||
|
val nip65ListNote = cache.getOrCreateAddressableNote(getNIP65RelayListAddress())
|
||||||
|
|
||||||
|
fun getNIP65RelayListAddress() = AdvertisedRelayListEvent.createAddress(signer.pubKey)
|
||||||
|
|
||||||
|
fun getNIP65RelayListFlow(): StateFlow<NoteState> = nip65ListNote.flow().metadata.stateFlow
|
||||||
|
|
||||||
|
fun getNIP65RelayList(): AdvertisedRelayListEvent? = nip65ListNote.event as? AdvertisedRelayListEvent
|
||||||
|
|
||||||
|
fun nip65Event(note: Note) = note.event as? AdvertisedRelayListEvent ?: settings.backupNIP65RelayList
|
||||||
|
|
||||||
|
fun normalizeNIP65WriteRelayListWithBackup(note: Note): Set<NormalizedRelayUrl> = nip65Event(note)?.writeRelaysNorm()?.toSet() ?: settings.defaultOutboxRelays
|
||||||
|
|
||||||
|
fun normalizeNIP65ReadRelayListWithBackup(note: Note): Set<NormalizedRelayUrl> = nip65Event(note)?.readRelaysNorm()?.toSet() ?: settings.defaultInboxRelays
|
||||||
|
|
||||||
|
fun normalizeNIP65WriteRelayListNoDefaults(note: Note): Set<NormalizedRelayUrl> = nip65Event(note)?.writeRelaysNorm()?.toSet() ?: emptySet()
|
||||||
|
|
||||||
|
fun normalizeNIP65ReadRelayListNoDefaults(note: Note): Set<NormalizedRelayUrl> = nip65Event(note)?.readRelaysNorm()?.toSet() ?: emptySet()
|
||||||
|
|
||||||
|
fun normalizeNIP65AllRelayListWithBackup(note: Note): Set<NormalizedRelayUrl> = nip65Event(note)?.relays()?.map { it.relayUrl }?.toSet() ?: settings.defaultOutboxRelays
|
||||||
|
|
||||||
|
fun normalizeNIP65AllRelayListWithBackupNoDefaults(note: Note): Set<NormalizedRelayUrl> = nip65Event(note)?.relays()?.map { it.relayUrl }?.toSet() ?: emptySet()
|
||||||
|
|
||||||
|
val outboxFlow =
|
||||||
|
getNIP65RelayListFlow()
|
||||||
|
.map { normalizeNIP65WriteRelayListWithBackup(it.note) }
|
||||||
|
.onStart { emit(normalizeNIP65WriteRelayListWithBackup(nip65ListNote)) }
|
||||||
|
.flowOn(Dispatchers.IO)
|
||||||
|
.stateIn(
|
||||||
|
scope,
|
||||||
|
SharingStarted.Eagerly,
|
||||||
|
emptySet(),
|
||||||
|
)
|
||||||
|
|
||||||
|
val inboxFlow =
|
||||||
|
getNIP65RelayListFlow()
|
||||||
|
.map { normalizeNIP65ReadRelayListWithBackup(it.note) }
|
||||||
|
.onStart { emit(normalizeNIP65ReadRelayListWithBackup(nip65ListNote)) }
|
||||||
|
.flowOn(Dispatchers.IO)
|
||||||
|
.stateIn(
|
||||||
|
scope,
|
||||||
|
SharingStarted.Eagerly,
|
||||||
|
emptySet(),
|
||||||
|
)
|
||||||
|
|
||||||
|
val outboxFlowNoDefaults =
|
||||||
|
getNIP65RelayListFlow()
|
||||||
|
.map { normalizeNIP65WriteRelayListNoDefaults(it.note) }
|
||||||
|
.onStart { emit(normalizeNIP65WriteRelayListNoDefaults(nip65ListNote)) }
|
||||||
|
.flowOn(Dispatchers.IO)
|
||||||
|
.stateIn(
|
||||||
|
scope,
|
||||||
|
SharingStarted.Eagerly,
|
||||||
|
emptySet(),
|
||||||
|
)
|
||||||
|
|
||||||
|
val inboxFlowNoDefaults =
|
||||||
|
getNIP65RelayListFlow()
|
||||||
|
.map { normalizeNIP65ReadRelayListNoDefaults(it.note) }
|
||||||
|
.onStart { emit(normalizeNIP65ReadRelayListNoDefaults(nip65ListNote)) }
|
||||||
|
.flowOn(Dispatchers.IO)
|
||||||
|
.stateIn(
|
||||||
|
scope,
|
||||||
|
SharingStarted.Eagerly,
|
||||||
|
emptySet(),
|
||||||
|
)
|
||||||
|
|
||||||
|
val allFlowNoDefaults =
|
||||||
|
getNIP65RelayListFlow()
|
||||||
|
.map { normalizeNIP65AllRelayListWithBackupNoDefaults(it.note) }
|
||||||
|
.onStart { emit(normalizeNIP65AllRelayListWithBackupNoDefaults(nip65ListNote)) }
|
||||||
|
.flowOn(Dispatchers.IO)
|
||||||
|
.stateIn(
|
||||||
|
scope,
|
||||||
|
SharingStarted.Eagerly,
|
||||||
|
emptySet(),
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun saveRelayList(relays: List<AdvertisedRelayInfo>): AdvertisedRelayListEvent {
|
||||||
|
val nip65RelayList = getNIP65RelayList()
|
||||||
|
|
||||||
|
return if (nip65RelayList != null) {
|
||||||
|
AdvertisedRelayListEvent.replaceRelayListWith(
|
||||||
|
earlierVersion = nip65RelayList,
|
||||||
|
newRelays = relays,
|
||||||
|
signer = signer,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
AdvertisedRelayListEvent.createFromScratch(
|
||||||
|
relays = relays,
|
||||||
|
signer = signer,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
settings.backupNIP65RelayList?.let {
|
||||||
|
Log.d("AccountRegisterObservers", "Loading saved nip65 relay list ${it.toJson()}")
|
||||||
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
|
scope.launch(Dispatchers.IO) { cache.justConsumeMyOwnEvent(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.launch(Dispatchers.IO) {
|
||||||
|
Log.d("AccountRegisterObservers", "NIP-65 Relay List Collector Start")
|
||||||
|
getNIP65RelayListFlow().collect {
|
||||||
|
Log.d("AccountRegisterObservers", "Updating NIP-65 List for ${signer.pubKey}")
|
||||||
|
(it.note.event as? AdvertisedRelayListEvent)?.let {
|
||||||
|
settings.updateNIP65RelayList(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user