From 597fec5c4356ce03a0e1c1cf0f7ac5f3c53c20bb Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Mon, 14 Jul 2025 11:19:12 -0400 Subject: [PATCH] Removes lazy StateFlows due to inconsistent starting values. --- .../vitorpamplona/amethyst/model/Account.kt | 2 +- .../edits/PrivateStorageRelayListState.kt | 5 ++-- .../model/emphChat/EphemeralChatListState.kt | 3 +-- .../FollowListOutboxRelays.kt | 25 +++++++++++-------- .../model/nip02FollowLists/FollowListState.kt | 1 + .../nip28PublicChats/PublicChatListState.kt | 8 +++--- .../model/nip30CustomEmojis/EmojiPackState.kt | 21 +++++++++++----- .../model/nip51Lists/GeohashListState.kt | 3 +-- .../model/nip51Lists/HashtagListState.kt | 3 +-- .../model/nip51Lists/HiddenUsersState.kt | 23 +++++++++-------- .../nip72Communities/CommunityListState.kt | 12 ++++----- .../amethyst/ui/screen/FollowListState.kt | 10 ++++++-- .../ui/screen/loggedIn/AccountViewModel.kt | 23 ++++++++++++++--- .../quartz/nip01Core/core/Event.kt | 2 +- .../nip01Core/relay/client/NostrClient.kt | 3 --- 15 files changed, 89 insertions(+), 55 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt index 92e400490..0baa6cd1f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt @@ -2266,7 +2266,7 @@ class Account( val oldHashtags = contactList?.hashtags()?.toSet() if (oldHashtags != null && oldHashtags.isNotEmpty()) { - Log.d("DB UPGRADE", "Migrating List with ${oldHashtags.size} old communities ") + Log.d("DB UPGRADE", "Migrating List with ${oldHashtags.size} old hashtags ") val existingHashtags = hashtagList.flow.value val needsToUpgrade = oldHashtags.filter { it !in existingHashtags } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/edits/PrivateStorageRelayListState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/edits/PrivateStorageRelayListState.kt index 5c7510c8c..43af1fdf9 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/edits/PrivateStorageRelayListState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/edits/PrivateStorageRelayListState.kt @@ -63,8 +63,9 @@ class PrivateStorageRelayListState( val flow = getPrivateOutboxRelayListFlow() .map { normalizePrivateOutboxRelayListWithBackup(it.note) } - .onStart { emit(normalizePrivateOutboxRelayListWithBackup(getPrivateOutboxRelayListNote())) } - .flowOn(Dispatchers.Default) + .onStart { + emit(normalizePrivateOutboxRelayListWithBackup(getPrivateOutboxRelayListNote())) + }.flowOn(Dispatchers.Default) .stateIn( scope, SharingStarted.Eagerly, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/emphChat/EphemeralChatListState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/emphChat/EphemeralChatListState.kt index 1117e70fd..07d22c658 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/emphChat/EphemeralChatListState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/emphChat/EphemeralChatListState.kt @@ -72,7 +72,7 @@ class EphemeralChatListState( } ?: emptySet() @OptIn(ExperimentalCoroutinesApi::class) - val liveEphemeralChatList: StateFlow> by lazy { + val liveEphemeralChatList: StateFlow> = getEphemeralChatListFlow() .transformLatest { noteState -> emit(ephemeralChatListWithBackup(noteState.note)) @@ -84,7 +84,6 @@ class EphemeralChatListState( SharingStarted.Eagerly, emptySet(), ) - } fun follow( channel: EphemeralChatChannel, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip02FollowLists/FollowListOutboxRelays.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip02FollowLists/FollowListOutboxRelays.kt index 7bfeaa71b..fde92f503 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip02FollowLists/FollowListOutboxRelays.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip02FollowLists/FollowListOutboxRelays.kt @@ -81,11 +81,14 @@ class FollowListOutboxRelays( emitAll(relayListFlows) }.onStart { val blocked = blockedRelayList.flow.value.toSet() - kind3Follows.flow.value.authors - .mapNotNull { - getNIP65RelayList(it)?.writeRelaysNorm()?.minus(blocked) - }.flatten() - .toSet() + val authors = kind3Follows.flow.value.authors + val perRelay = + authors + .mapNotNull { + getNIP65RelayList(it)?.writeRelaysNorm()?.minus(blocked) + }.flatten() + .toSet() + emit(perRelay) }.flowOn(Dispatchers.Default) .stateIn( scope, @@ -99,11 +102,13 @@ class FollowListOutboxRelays( .map { relayList -> relayList.map { it.url }.toSet() }.onStart { - kind3Follows.flow.value.authors - .mapNotNull { - getNIP65RelayList(it)?.writeRelaysNorm()?.map { it.url }?.toSet() - }.flatten() - .toSet() + emit( + kind3Follows.flow.value.authors + .mapNotNull { + getNIP65RelayList(it)?.writeRelaysNorm()?.map { it.url }?.toSet() + }.flatten() + .toSet(), + ) }.flowOn(Dispatchers.Default) .stateIn( scope, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip02FollowLists/FollowListState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip02FollowLists/FollowListState.kt index e8d8c3182..59bedaa1e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip02FollowLists/FollowListState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip02FollowLists/FollowListState.kt @@ -71,6 +71,7 @@ class FollowListState( .stateIn( scope, SharingStarted.Eagerly, + // this has priority. buildKind3Follows(getFollowListEvent() ?: settings.backupContactList), ) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip28PublicChats/PublicChatListState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip28PublicChats/PublicChatListState.kt index 96e6693f2..109193c0a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip28PublicChats/PublicChatListState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip28PublicChats/PublicChatListState.kt @@ -74,7 +74,7 @@ class PublicChatListState( } ?: emptySet() @OptIn(ExperimentalCoroutinesApi::class) - val flow: StateFlow> by lazy { + val flow: StateFlow> = getChannelListFlow() .transformLatest { noteState -> emit(publicChatListWithBackup(noteState.note)) @@ -86,20 +86,20 @@ class PublicChatListState( SharingStarted.Eagerly, emptySet(), ) - } @OptIn(ExperimentalCoroutinesApi::class) - val flowSet: StateFlow> by lazy { + val flowSet: StateFlow> = flow .map { it.mapTo(mutableSetOf()) { it.eventId } + }.onStart { + emit(flow.value.mapTo(mutableSetOf()) { it.eventId }) }.flowOn(Dispatchers.Default) .stateIn( scope, SharingStarted.Eagerly, emptySet(), ) - } fun follow( channel: PublicChatChannel, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip30CustomEmojis/EmojiPackState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip30CustomEmojis/EmojiPackState.kt index 9d43b89ec..e81567ae9 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip30CustomEmojis/EmojiPackState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip30CustomEmojis/EmojiPackState.kt @@ -38,6 +38,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combineTransform import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.transformLatest @@ -68,17 +69,18 @@ class EmojiPackState( } @OptIn(ExperimentalCoroutinesApi::class) - val flow: StateFlow>?> by lazy { + val flow: StateFlow>?> = getEmojiPackSelectionFlow() .transformLatest { emit(convertEmojiSelectionPack(it.note.event as? EmojiPackSelectionEvent)) + }.onStart { + emit(convertEmojiSelectionPack(getEmojiPackSelection())) }.flowOn(Dispatchers.Default) .stateIn( scope, SharingStarted.Eagerly, - convertEmojiSelectionPack(getEmojiPackSelection()), + emptyList(), ) - } fun convertEmojiPack(pack: EmojiPackEvent): List = pack.taggedEmojis().map { @@ -98,7 +100,7 @@ class EmojiPackState( .distinctBy { it.link } @OptIn(ExperimentalCoroutinesApi::class) - val myEmojis by lazy { + val myEmojis = flow .transformLatest { emojiList -> if (emojiList != null) { @@ -110,13 +112,20 @@ class EmojiPackState( } else { emit(emptyList()) } + }.onStart { + emit( + mergePack( + convertEmojiSelectionPack( + getEmojiPackSelection(), + )?.map { it.value }?.toTypedArray() ?: emptyArray(), + ), + ) }.flowOn(Dispatchers.Default) .stateIn( scope, SharingStarted.Eagerly, - mergePack(convertEmojiSelectionPack(getEmojiPackSelection())?.map { it.value }?.toTypedArray() ?: emptyArray()), + emptyList(), ) - } fun addEmojiPack( emojiPack: Note, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip51Lists/GeohashListState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip51Lists/GeohashListState.kt index 3d2e4e6ec..096a55fca 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip51Lists/GeohashListState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip51Lists/GeohashListState.kt @@ -70,7 +70,7 @@ class GeohashListState( } ?: emptySet() @OptIn(ExperimentalCoroutinesApi::class) - val flow: StateFlow> by lazy { + val flow: StateFlow> = getGeohashListFlow() .transformLatest { noteState -> emit(geohashListWithBackup(noteState.note)) @@ -82,7 +82,6 @@ class GeohashListState( SharingStarted.Eagerly, emptySet(), ) - } fun follow( geohashs: List, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip51Lists/HashtagListState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip51Lists/HashtagListState.kt index 6b3e09b29..acf863a10 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip51Lists/HashtagListState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip51Lists/HashtagListState.kt @@ -70,7 +70,7 @@ class HashtagListState( } ?: emptySet() @OptIn(ExperimentalCoroutinesApi::class) - val flow: StateFlow> by lazy { + val flow: StateFlow> = getHashtagListFlow() .transformLatest { noteState -> emit(hashtagListWithBackup(noteState.note)) @@ -82,7 +82,6 @@ class HashtagListState( SharingStarted.Eagerly, emptySet(), ) - } fun follow( hashtags: List, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip51Lists/HiddenUsersState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip51Lists/HiddenUsersState.kt index 9888ba5e0..d0fd40434 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip51Lists/HiddenUsersState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip51Lists/HiddenUsersState.kt @@ -33,9 +33,9 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combineTransform import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update -import kotlinx.coroutines.runBlocking class HiddenUsersState( val muteList: StateFlow, @@ -73,7 +73,7 @@ class HiddenUsersState( showSensitiveContent = showSensitiveContent, ) - val flow: StateFlow by lazy { + val flow: StateFlow = combineTransform( blockList, muteList, @@ -82,20 +82,21 @@ class HiddenUsersState( ) { blockList, muteList, transientHiddenUsers, showSensitiveContent -> checkNotInMainThread() emit(assembleLiveHiddenUsers(blockList, muteList, transientHiddenUsers, showSensitiveContent)) + }.onStart { + emit( + assembleLiveHiddenUsers( + blockList.value, + muteList.value, + transientHiddenUsers.value, + settings.syncedSettings.security.showSensitiveContent.value, + ), + ) }.flowOn(Dispatchers.Default) .stateIn( scope, SharingStarted.Eagerly, - runBlocking { - assembleLiveHiddenUsers( - blockList.value, - muteList.value, - transientHiddenUsers.value, - settings.syncedSettings.security.showSensitiveContent.value, - ) - }, + LiveHiddenUsers(emptySet(), emptySet(), emptySet(), null), ) - } fun resetTransientUsers() { transientHiddenUsers.update { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip72Communities/CommunityListState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip72Communities/CommunityListState.kt index 84f127155..2690fb53b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip72Communities/CommunityListState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip72Communities/CommunityListState.kt @@ -73,7 +73,7 @@ class CommunityListState( } ?: emptySet() @OptIn(ExperimentalCoroutinesApi::class) - val flow: StateFlow> by lazy { + val flow: StateFlow> = getCommunityListFlow() .transformLatest { noteState -> emit(communityListWithBackup(noteState.note)) @@ -85,20 +85,20 @@ class CommunityListState( SharingStarted.Eagerly, emptySet(), ) - } @OptIn(ExperimentalCoroutinesApi::class) - val flowSet: StateFlow> by lazy { + val flowSet: StateFlow> = flow - .map { - it.mapTo(mutableSetOf()) { it.addressId } + .map { hint -> + hint.mapTo(mutableSetOf()) { it.addressId } + }.onStart { + emit(flow.value.mapTo(mutableSetOf()) { it.addressId }) }.flowOn(Dispatchers.Default) .stateIn( scope, SharingStarted.Eagerly, emptySet(), ) - } fun follow( communities: List, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/FollowListState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/FollowListState.kt index 11203eddf..b55718b84 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/FollowListState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/FollowListState.kt @@ -237,8 +237,14 @@ class FollowListState( ) } - val kind3GlobalPeopleRoutes = _kind3GlobalPeopleRoutes.flowOn(Dispatchers.Default).stateIn(viewModelScope, SharingStarted.Eagerly, defaultLists) - val kind3GlobalPeople = _kind3GlobalPeople.flowOn(Dispatchers.Default).stateIn(viewModelScope, SharingStarted.Eagerly, defaultLists) + val kind3GlobalPeopleRoutes = + _kind3GlobalPeopleRoutes + .flowOn(Dispatchers.Default) + .stateIn(viewModelScope, SharingStarted.Eagerly, defaultLists) + val kind3GlobalPeople = + _kind3GlobalPeople + .flowOn(Dispatchers.Default) + .stateIn(viewModelScope, SharingStarted.Eagerly, defaultLists) suspend fun initializeSuspend() { checkNotInMainThread() diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt index b919be4d9..024aab78d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt @@ -194,7 +194,10 @@ class AccountViewModel( emit(newestItemCreatedAt != null && newestItemCreatedAt > lastRead) } - val notificationHasNewItemsFlow = notificationHasNewItems.flowOn(Dispatchers.Default).stateIn(viewModelScope, SharingStarted.Eagerly, false) + val notificationHasNewItemsFlow = + notificationHasNewItems + .flowOn(Dispatchers.Default) + .stateIn(viewModelScope, SharingStarted.Eagerly, false) @OptIn(ExperimentalCoroutinesApi::class) val messagesHasNewItems = @@ -225,7 +228,10 @@ class AccountViewModel( } } - val messagesHasNewItemsFlow = messagesHasNewItems.flowOn(Dispatchers.Default).stateIn(viewModelScope, SharingStarted.Eagerly, false) + val messagesHasNewItemsFlow = + messagesHasNewItems + .flowOn(Dispatchers.Default) + .stateIn(viewModelScope, SharingStarted.Eagerly, false) @OptIn(ExperimentalCoroutinesApi::class) val homeHasNewItems = @@ -245,7 +251,10 @@ class AccountViewModel( emit(newestItemCreatedAt != null && newestItemCreatedAt > lastRead) } - val homeHasNewItemsFlow = homeHasNewItems.flowOn(Dispatchers.Default).stateIn(viewModelScope, SharingStarted.Eagerly, false) + val homeHasNewItemsFlow = + homeHasNewItems + .flowOn(Dispatchers.Default) + .stateIn(viewModelScope, SharingStarted.Eagerly, false) val hasNewItems = mapOf( @@ -359,6 +368,14 @@ class AccountViewModel( note.flow().reports.stateFlow, ) { hiddenUsers, followingUsers, autor, metadata, reports -> emit(isNoteAcceptable(metadata.note, hiddenUsers, followingUsers.authors)) + }.onStart { + emit( + isNoteAcceptable( + note, + account.hiddenUsers.flow.value, + account.kind3FollowList.flow.value.authors, + ), + ) }.flowOn(Dispatchers.Default) .stateIn( viewModelScope, diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/core/Event.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/core/Event.kt index cea5d7867..806b49996 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/core/Event.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/core/Event.kt @@ -63,7 +63,7 @@ open class Event( try { fromJson(json) } catch (e: Exception) { - Log.e("Event", "Unable to parse event JSON: $json", e) + Log.w("Event", "Unable to parse event JSON: $json", e) null } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/relay/client/NostrClient.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/relay/client/NostrClient.kt index 7371deaae..e2b587d9b 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/relay/client/NostrClient.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/relay/client/NostrClient.kt @@ -41,7 +41,6 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.sample import kotlinx.coroutines.flow.stateIn @@ -93,8 +92,6 @@ class NostrClient( eventOutbox.relays, ) { reqs, counts, outbox -> reqs + counts + outbox - }.onStart { - activeRequests.relays.value + activeCounts.relays.value + eventOutbox.relays.value }.sample(300) .onEach { relayPool.updatePool(it)