Removes lazy StateFlows due to inconsistent starting values.

This commit is contained in:
Vitor Pamplona
2025-07-14 11:19:12 -04:00
parent 909eb62db6
commit 597fec5c43
15 changed files with 89 additions and 55 deletions
@@ -2266,7 +2266,7 @@ class Account(
val oldHashtags = contactList?.hashtags()?.toSet() val oldHashtags = contactList?.hashtags()?.toSet()
if (oldHashtags != null && oldHashtags.isNotEmpty()) { 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 existingHashtags = hashtagList.flow.value
val needsToUpgrade = oldHashtags.filter { it !in existingHashtags } val needsToUpgrade = oldHashtags.filter { it !in existingHashtags }
@@ -63,8 +63,9 @@ class PrivateStorageRelayListState(
val flow = val flow =
getPrivateOutboxRelayListFlow() getPrivateOutboxRelayListFlow()
.map { normalizePrivateOutboxRelayListWithBackup(it.note) } .map { normalizePrivateOutboxRelayListWithBackup(it.note) }
.onStart { emit(normalizePrivateOutboxRelayListWithBackup(getPrivateOutboxRelayListNote())) } .onStart {
.flowOn(Dispatchers.Default) emit(normalizePrivateOutboxRelayListWithBackup(getPrivateOutboxRelayListNote()))
}.flowOn(Dispatchers.Default)
.stateIn( .stateIn(
scope, scope,
SharingStarted.Eagerly, SharingStarted.Eagerly,
@@ -72,7 +72,7 @@ class EphemeralChatListState(
} ?: emptySet() } ?: emptySet()
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
val liveEphemeralChatList: StateFlow<Set<RoomId>> by lazy { val liveEphemeralChatList: StateFlow<Set<RoomId>> =
getEphemeralChatListFlow() getEphemeralChatListFlow()
.transformLatest { noteState -> .transformLatest { noteState ->
emit(ephemeralChatListWithBackup(noteState.note)) emit(ephemeralChatListWithBackup(noteState.note))
@@ -84,7 +84,6 @@ class EphemeralChatListState(
SharingStarted.Eagerly, SharingStarted.Eagerly,
emptySet(), emptySet(),
) )
}
fun follow( fun follow(
channel: EphemeralChatChannel, channel: EphemeralChatChannel,
@@ -81,11 +81,14 @@ class FollowListOutboxRelays(
emitAll(relayListFlows) emitAll(relayListFlows)
}.onStart { }.onStart {
val blocked = blockedRelayList.flow.value.toSet() val blocked = blockedRelayList.flow.value.toSet()
kind3Follows.flow.value.authors val authors = kind3Follows.flow.value.authors
.mapNotNull { val perRelay =
getNIP65RelayList(it)?.writeRelaysNorm()?.minus(blocked) authors
}.flatten() .mapNotNull {
.toSet() getNIP65RelayList(it)?.writeRelaysNorm()?.minus(blocked)
}.flatten()
.toSet()
emit(perRelay)
}.flowOn(Dispatchers.Default) }.flowOn(Dispatchers.Default)
.stateIn( .stateIn(
scope, scope,
@@ -99,11 +102,13 @@ class FollowListOutboxRelays(
.map { relayList -> .map { relayList ->
relayList.map { it.url }.toSet() relayList.map { it.url }.toSet()
}.onStart { }.onStart {
kind3Follows.flow.value.authors emit(
.mapNotNull { kind3Follows.flow.value.authors
getNIP65RelayList(it)?.writeRelaysNorm()?.map { it.url }?.toSet() .mapNotNull {
}.flatten() getNIP65RelayList(it)?.writeRelaysNorm()?.map { it.url }?.toSet()
.toSet() }.flatten()
.toSet(),
)
}.flowOn(Dispatchers.Default) }.flowOn(Dispatchers.Default)
.stateIn( .stateIn(
scope, scope,
@@ -71,6 +71,7 @@ class FollowListState(
.stateIn( .stateIn(
scope, scope,
SharingStarted.Eagerly, SharingStarted.Eagerly,
// this has priority.
buildKind3Follows(getFollowListEvent() ?: settings.backupContactList), buildKind3Follows(getFollowListEvent() ?: settings.backupContactList),
) )
@@ -74,7 +74,7 @@ class PublicChatListState(
} ?: emptySet() } ?: emptySet()
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
val flow: StateFlow<Set<EventIdHint>> by lazy { val flow: StateFlow<Set<EventIdHint>> =
getChannelListFlow() getChannelListFlow()
.transformLatest { noteState -> .transformLatest { noteState ->
emit(publicChatListWithBackup(noteState.note)) emit(publicChatListWithBackup(noteState.note))
@@ -86,20 +86,20 @@ class PublicChatListState(
SharingStarted.Eagerly, SharingStarted.Eagerly,
emptySet(), emptySet(),
) )
}
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
val flowSet: StateFlow<Set<HexKey>> by lazy { val flowSet: StateFlow<Set<HexKey>> =
flow flow
.map { .map {
it.mapTo(mutableSetOf()) { it.eventId } it.mapTo(mutableSetOf()) { it.eventId }
}.onStart {
emit(flow.value.mapTo(mutableSetOf()) { it.eventId })
}.flowOn(Dispatchers.Default) }.flowOn(Dispatchers.Default)
.stateIn( .stateIn(
scope, scope,
SharingStarted.Eagerly, SharingStarted.Eagerly,
emptySet(), emptySet(),
) )
}
fun follow( fun follow(
channel: PublicChatChannel, channel: PublicChatChannel,
@@ -38,6 +38,7 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combineTransform import kotlinx.coroutines.flow.combineTransform
import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.transformLatest import kotlinx.coroutines.flow.transformLatest
@@ -68,17 +69,18 @@ class EmojiPackState(
} }
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
val flow: StateFlow<List<StateFlow<NoteState>>?> by lazy { val flow: StateFlow<List<StateFlow<NoteState>>?> =
getEmojiPackSelectionFlow() getEmojiPackSelectionFlow()
.transformLatest { .transformLatest {
emit(convertEmojiSelectionPack(it.note.event as? EmojiPackSelectionEvent)) emit(convertEmojiSelectionPack(it.note.event as? EmojiPackSelectionEvent))
}.onStart {
emit(convertEmojiSelectionPack(getEmojiPackSelection()))
}.flowOn(Dispatchers.Default) }.flowOn(Dispatchers.Default)
.stateIn( .stateIn(
scope, scope,
SharingStarted.Eagerly, SharingStarted.Eagerly,
convertEmojiSelectionPack(getEmojiPackSelection()), emptyList(),
) )
}
fun convertEmojiPack(pack: EmojiPackEvent): List<EmojiMedia> = fun convertEmojiPack(pack: EmojiPackEvent): List<EmojiMedia> =
pack.taggedEmojis().map { pack.taggedEmojis().map {
@@ -98,7 +100,7 @@ class EmojiPackState(
.distinctBy { it.link } .distinctBy { it.link }
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
val myEmojis by lazy { val myEmojis =
flow flow
.transformLatest { emojiList -> .transformLatest { emojiList ->
if (emojiList != null) { if (emojiList != null) {
@@ -110,13 +112,20 @@ class EmojiPackState(
} else { } else {
emit(emptyList()) emit(emptyList())
} }
}.onStart {
emit(
mergePack(
convertEmojiSelectionPack(
getEmojiPackSelection(),
)?.map { it.value }?.toTypedArray() ?: emptyArray(),
),
)
}.flowOn(Dispatchers.Default) }.flowOn(Dispatchers.Default)
.stateIn( .stateIn(
scope, scope,
SharingStarted.Eagerly, SharingStarted.Eagerly,
mergePack(convertEmojiSelectionPack(getEmojiPackSelection())?.map { it.value }?.toTypedArray() ?: emptyArray()), emptyList(),
) )
}
fun addEmojiPack( fun addEmojiPack(
emojiPack: Note, emojiPack: Note,
@@ -70,7 +70,7 @@ class GeohashListState(
} ?: emptySet() } ?: emptySet()
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
val flow: StateFlow<Set<String>> by lazy { val flow: StateFlow<Set<String>> =
getGeohashListFlow() getGeohashListFlow()
.transformLatest { noteState -> .transformLatest { noteState ->
emit(geohashListWithBackup(noteState.note)) emit(geohashListWithBackup(noteState.note))
@@ -82,7 +82,6 @@ class GeohashListState(
SharingStarted.Eagerly, SharingStarted.Eagerly,
emptySet(), emptySet(),
) )
}
fun follow( fun follow(
geohashs: List<String>, geohashs: List<String>,
@@ -70,7 +70,7 @@ class HashtagListState(
} ?: emptySet() } ?: emptySet()
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
val flow: StateFlow<Set<String>> by lazy { val flow: StateFlow<Set<String>> =
getHashtagListFlow() getHashtagListFlow()
.transformLatest { noteState -> .transformLatest { noteState ->
emit(hashtagListWithBackup(noteState.note)) emit(hashtagListWithBackup(noteState.note))
@@ -82,7 +82,6 @@ class HashtagListState(
SharingStarted.Eagerly, SharingStarted.Eagerly,
emptySet(), emptySet(),
) )
}
fun follow( fun follow(
hashtags: List<String>, hashtags: List<String>,
@@ -33,9 +33,9 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combineTransform import kotlinx.coroutines.flow.combineTransform
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.runBlocking
class HiddenUsersState( class HiddenUsersState(
val muteList: StateFlow<PeopleListEvent.UsersAndWords>, val muteList: StateFlow<PeopleListEvent.UsersAndWords>,
@@ -73,7 +73,7 @@ class HiddenUsersState(
showSensitiveContent = showSensitiveContent, showSensitiveContent = showSensitiveContent,
) )
val flow: StateFlow<LiveHiddenUsers> by lazy { val flow: StateFlow<LiveHiddenUsers> =
combineTransform( combineTransform(
blockList, blockList,
muteList, muteList,
@@ -82,20 +82,21 @@ class HiddenUsersState(
) { blockList, muteList, transientHiddenUsers, showSensitiveContent -> ) { blockList, muteList, transientHiddenUsers, showSensitiveContent ->
checkNotInMainThread() checkNotInMainThread()
emit(assembleLiveHiddenUsers(blockList, muteList, transientHiddenUsers, showSensitiveContent)) emit(assembleLiveHiddenUsers(blockList, muteList, transientHiddenUsers, showSensitiveContent))
}.onStart {
emit(
assembleLiveHiddenUsers(
blockList.value,
muteList.value,
transientHiddenUsers.value,
settings.syncedSettings.security.showSensitiveContent.value,
),
)
}.flowOn(Dispatchers.Default) }.flowOn(Dispatchers.Default)
.stateIn( .stateIn(
scope, scope,
SharingStarted.Eagerly, SharingStarted.Eagerly,
runBlocking { LiveHiddenUsers(emptySet(), emptySet(), emptySet(), null),
assembleLiveHiddenUsers(
blockList.value,
muteList.value,
transientHiddenUsers.value,
settings.syncedSettings.security.showSensitiveContent.value,
)
},
) )
}
fun resetTransientUsers() { fun resetTransientUsers() {
transientHiddenUsers.update { transientHiddenUsers.update {
@@ -73,7 +73,7 @@ class CommunityListState(
} ?: emptySet() } ?: emptySet()
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
val flow: StateFlow<Set<AddressHint>> by lazy { val flow: StateFlow<Set<AddressHint>> =
getCommunityListFlow() getCommunityListFlow()
.transformLatest { noteState -> .transformLatest { noteState ->
emit(communityListWithBackup(noteState.note)) emit(communityListWithBackup(noteState.note))
@@ -85,20 +85,20 @@ class CommunityListState(
SharingStarted.Eagerly, SharingStarted.Eagerly,
emptySet(), emptySet(),
) )
}
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
val flowSet: StateFlow<Set<String>> by lazy { val flowSet: StateFlow<Set<String>> =
flow flow
.map { .map { hint ->
it.mapTo(mutableSetOf()) { it.addressId } hint.mapTo(mutableSetOf()) { it.addressId }
}.onStart {
emit(flow.value.mapTo(mutableSetOf()) { it.addressId })
}.flowOn(Dispatchers.Default) }.flowOn(Dispatchers.Default)
.stateIn( .stateIn(
scope, scope,
SharingStarted.Eagerly, SharingStarted.Eagerly,
emptySet(), emptySet(),
) )
}
fun follow( fun follow(
communities: List<AddressableNote>, communities: List<AddressableNote>,
@@ -237,8 +237,14 @@ class FollowListState(
) )
} }
val kind3GlobalPeopleRoutes = _kind3GlobalPeopleRoutes.flowOn(Dispatchers.Default).stateIn(viewModelScope, SharingStarted.Eagerly, defaultLists) val kind3GlobalPeopleRoutes =
val kind3GlobalPeople = _kind3GlobalPeople.flowOn(Dispatchers.Default).stateIn(viewModelScope, SharingStarted.Eagerly, defaultLists) _kind3GlobalPeopleRoutes
.flowOn(Dispatchers.Default)
.stateIn(viewModelScope, SharingStarted.Eagerly, defaultLists)
val kind3GlobalPeople =
_kind3GlobalPeople
.flowOn(Dispatchers.Default)
.stateIn(viewModelScope, SharingStarted.Eagerly, defaultLists)
suspend fun initializeSuspend() { suspend fun initializeSuspend() {
checkNotInMainThread() checkNotInMainThread()
@@ -194,7 +194,10 @@ class AccountViewModel(
emit(newestItemCreatedAt != null && newestItemCreatedAt > lastRead) 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) @OptIn(ExperimentalCoroutinesApi::class)
val messagesHasNewItems = 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) @OptIn(ExperimentalCoroutinesApi::class)
val homeHasNewItems = val homeHasNewItems =
@@ -245,7 +251,10 @@ class AccountViewModel(
emit(newestItemCreatedAt != null && newestItemCreatedAt > lastRead) 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 = val hasNewItems =
mapOf( mapOf(
@@ -359,6 +368,14 @@ class AccountViewModel(
note.flow().reports.stateFlow, note.flow().reports.stateFlow,
) { hiddenUsers, followingUsers, autor, metadata, reports -> ) { hiddenUsers, followingUsers, autor, metadata, reports ->
emit(isNoteAcceptable(metadata.note, hiddenUsers, followingUsers.authors)) emit(isNoteAcceptable(metadata.note, hiddenUsers, followingUsers.authors))
}.onStart {
emit(
isNoteAcceptable(
note,
account.hiddenUsers.flow.value,
account.kind3FollowList.flow.value.authors,
),
)
}.flowOn(Dispatchers.Default) }.flowOn(Dispatchers.Default)
.stateIn( .stateIn(
viewModelScope, viewModelScope,
@@ -63,7 +63,7 @@ open class Event(
try { try {
fromJson(json) fromJson(json)
} catch (e: Exception) { } catch (e: Exception) {
Log.e("Event", "Unable to parse event JSON: $json", e) Log.w("Event", "Unable to parse event JSON: $json", e)
null null
} }
@@ -41,7 +41,6 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.sample import kotlinx.coroutines.flow.sample
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
@@ -93,8 +92,6 @@ class NostrClient(
eventOutbox.relays, eventOutbox.relays,
) { reqs, counts, outbox -> ) { reqs, counts, outbox ->
reqs + counts + outbox reqs + counts + outbox
}.onStart {
activeRequests.relays.value + activeCounts.relays.value + eventOutbox.relays.value
}.sample(300) }.sample(300)
.onEach { .onEach {
relayPool.updatePool(it) relayPool.updatePool(it)