Merge branch 'main' into image-gallery-improve-layout

This commit is contained in:
davotoula
2025-10-23 15:01:29 +02:00
129 changed files with 412 additions and 427 deletions
+2 -1
View File
@@ -8,7 +8,8 @@ trim_trailing_whitespace = true
max_line_length = 5000
ij_kotlin_allow_trailing_comma=true
ij_kotlin_allow_trailing_comma_on_call_site=true
ktlint_function_naming_ignore_when_annotated_with=Composable, Test
ktlint_function_naming_ignore_when_annotated_with=Composable
ktlint_standard_no-unused-imports=enabled
[*.gradle]
indent_size = 4
@@ -86,7 +86,7 @@ class AppModules(
}
val applicationIOScope = CoroutineScope(Dispatchers.IO + SupervisorJob() + exceptionHandler)
val applicationDefaultScope = CoroutineScope(Dispatchers.Default + SupervisorJob() + exceptionHandler)
val applicationDefaultScope = CoroutineScope(Dispatchers.IO + SupervisorJob() + exceptionHandler)
// Blocking load of UI Preferences to avoid theme/language blinking
val uiPrefs by lazy {
@@ -38,8 +38,8 @@ import com.vitorpamplona.amethyst.model.nip01UserMetadata.UserMetadataState
import com.vitorpamplona.amethyst.model.nip02FollowLists.DeclaredFollowsPerOutboxRelay
import com.vitorpamplona.amethyst.model.nip02FollowLists.FollowListOutboxOrProxyRelays
import com.vitorpamplona.amethyst.model.nip02FollowLists.FollowListReusedOutboxOrProxyRelays
import com.vitorpamplona.amethyst.model.nip02FollowLists.FollowListState
import com.vitorpamplona.amethyst.model.nip02FollowLists.FollowsPerOutboxRelay
import com.vitorpamplona.amethyst.model.nip02FollowLists.Kind3FollowListState
import com.vitorpamplona.amethyst.model.nip03Timestamp.OtsState
import com.vitorpamplona.amethyst.model.nip17Dms.DmInboxRelayState
import com.vitorpamplona.amethyst.model.nip17Dms.DmRelayListState
@@ -205,6 +205,7 @@ import com.vitorpamplona.quartz.nip98HttpAuth.HTTPAuthorizationEvent
import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceEvent
import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceReplyEvent
import com.vitorpamplona.quartz.utils.Log
import com.vitorpamplona.quartz.utils.containsAny
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
@@ -265,7 +266,7 @@ class Account(
val blockedRelayListDecryptionCache = BlockedRelayListDecryptionCache(signer)
val blockedRelayList = BlockedRelayListState(signer, cache, blockedRelayListDecryptionCache, scope, settings)
val kind3FollowList = FollowListState(signer, cache, scope, settings)
val kind3FollowList = Kind3FollowListState(signer, cache, scope, settings)
val followSetsState = FollowSetState(signer, cache, scope)
val ephemeralChatListDecryptionCache = EphemeralChatListDecryptionCache(signer)
@@ -1677,6 +1678,16 @@ class Account(
note.countReportAuthorsBy(followingKeySet()) < 5 // if it has 5 reports by reliable users
}
fun isDecryptedContentHidden(noteEvent: PrivateDmEvent): Boolean =
if (hiddenUsers.flow.value.hiddenWordsCase
.isNotEmpty()
) {
val decrypted = privateDMDecryptionCache.cachedDM(noteEvent)
decrypted?.containsAny(hiddenUsers.flow.value.hiddenWordsCase) == true
} else {
false
}
fun isFollowing(user: User): Boolean = user.pubkeyHex in followingKeySet()
fun isFollowing(user: HexKey): Boolean = user in followingKeySet()
@@ -1761,7 +1772,7 @@ class Account(
fun observeDonatedInThisVersion() =
settings
.observeDonatedInVersion(BuildConfig.VERSION_NAME)
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(scope, SharingStarted.Eagerly, hasDonatedInThisVersion())
fun markDonatedInThisVersion() = settings.markDonatedInThisVersion(BuildConfig.VERSION_NAME)
@@ -1962,7 +1962,7 @@ object LocalCache : ILocalCache {
requestNote?.let { request -> zappedNote?.addZapPayment(request, note) }
GlobalScope.launch(Dispatchers.Default) {
GlobalScope.launch(Dispatchers.IO) {
responseCallback(event)
}
@@ -2913,10 +2913,10 @@ class LocalCacheFlow {
val deletedEventBundles = _deletedEventBundles.asSharedFlow() // read-only public view
// Refreshes observers in batches.
private val bundler = BundledInsert<Note>(1000, Dispatchers.Default)
private val bundler = BundledInsert<Note>(1000, Dispatchers.IO)
// Refreshes observers in batches.
private val bundler2 = BundledInsert<Note>(1000, Dispatchers.Default)
private val bundler2 = BundledInsert<Note>(1000, Dispatchers.IO)
fun newNote(newNote: Note) {
bundler.invalidateList(newNote) { bundledNewNotes ->
@@ -97,7 +97,7 @@ class AccountCacheState(
client = client,
scope =
CoroutineScope(
Dispatchers.Default +
Dispatchers.IO +
SupervisorJob() +
CoroutineExceptionHandler { _, throwable ->
Log.e("AccountCacheState", "Account ${signer.pubKey} caught exception: ${throwable.message}", throwable)
@@ -66,7 +66,7 @@ class PrivateStorageRelayListState(
.map { normalizePrivateOutboxRelayListWithBackup(it.note) }
.onStart {
emit(normalizePrivateOutboxRelayListWithBackup(privateOutboxListNote))
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -99,7 +99,7 @@ class PrivateStorageRelayListState(
}
}
scope.launch(Dispatchers.Default) {
scope.launch(Dispatchers.IO) {
Log.d("AccountRegisterObservers", "Private Home Relay List Collector Start")
getPrivateOutboxRelayListFlow().collect { noteState ->
Log.d("AccountRegisterObservers", "Updating Private Home Relay List for ${signer.pubKey}")
@@ -69,7 +69,7 @@ class EphemeralChatListState(
emit(ephemeralChatListWithBackup(noteState.note))
}.onStart {
emit(ephemeralChatListWithBackup(ephemeralChatListNote))
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -117,7 +117,7 @@ class EphemeralChatListState(
}
}
scope.launch(Dispatchers.Default) {
scope.launch(Dispatchers.IO) {
Log.d("AccountRegisterObservers", "EphemeralChatList Collector Start")
getEphemeralChatListFlow().collect { noteState ->
Log.d("AccountRegisterObservers", "EphemeralChatList List for ${signer.pubKey}")
@@ -43,7 +43,7 @@ class LocalRelayListState(
val flow =
settings.localRelayServers
.map { normalizeLocalRelayListWithBackup(it) }
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -43,7 +43,7 @@ class AccountHomeRelayState(
local.flow,
) { nip65Outbox, privateOutBox, localRelays ->
nip65Outbox + privateOutBox + localRelays
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -46,7 +46,7 @@ class AccountOutboxRelayState(
broadcast.flow,
) { nip65Outbox, privateOutBox, localRelays, broadcastRelays ->
nip65Outbox + privateOutBox + localRelays + broadcastRelays
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -40,7 +40,7 @@ class NotificationInboxRelayState(
localRelayList.flow,
) { nip65Inbox, localRelays ->
nip65Inbox + localRelays
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -22,7 +22,6 @@ package com.vitorpamplona.amethyst.model.nip01UserMetadata
import com.vitorpamplona.amethyst.model.AccountSettings
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.model.UserState
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
@@ -40,13 +39,14 @@ class UserMetadataState(
val scope: CoroutineScope,
val settings: AccountSettings,
) {
// Creates a long-term reference for this note so that the GC doesn't collect the note it self
val user = cache.getOrCreateUser(signer.pubKey)
// fun getEphemeralChatListAddress() = cache.getOrCreateUser(signer.pubKey)
fun getUserMetadataUser(): User = cache.getOrCreateUser(signer.pubKey)
fun getUserMetadataFlow(): StateFlow<UserState> = user.flow().metadata.stateFlow
fun getUserMetadataFlow(): StateFlow<UserState> = getUserMetadataUser().flow().metadata.stateFlow
fun getUserMetadataEvent(): MetadataEvent? = getUserMetadataUser().latestMetadata
fun getUserMetadataEvent(): MetadataEvent? = user.latestMetadata
suspend fun sendNewUserMetadata(
name: String? = null,
@@ -112,7 +112,7 @@ class UserMetadataState(
}
// saves contact list for the next time.
scope.launch(Dispatchers.Default) {
scope.launch(Dispatchers.IO) {
Log.d("AccountRegisterObservers", "Kind 0 Collector Start")
getUserMetadataFlow().collect {
Log.d("AccountRegisterObservers", "Updating Kind 0 ${it.user.toBestDisplayName()}")
@@ -37,7 +37,7 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.transformLatest
class DeclaredFollowsPerOutboxRelay(
kind3Follows: FollowListState,
kind3Follows: Kind3FollowListState,
val cache: LocalCache,
scope: CoroutineScope,
) {
@@ -55,7 +55,7 @@ class DeclaredFollowsPerOutboxRelay(
calculator.authorsPerRelaySnapshot(kind3Follows.flow.value.authors, cache) { it },
)
}.distinctUntilChanged()
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -42,7 +42,7 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.transformLatest
class FollowListOutboxOrProxyRelays(
kind3Follows: FollowListState,
kind3Follows: Kind3FollowListState,
blockedRelayList: BlockedRelayListState,
proxyRelayList: ProxyRelayListState,
val cache: LocalCache,
@@ -60,7 +60,7 @@ class FollowListOutboxOrProxyRelays(
OutboxRelayLoader(true).authorsPerRelaySnapshot(kind3Follows.flow.value.authors, cache) { it.keys },
)
}.distinctUntilChanged()
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -72,7 +72,7 @@ class FollowListOutboxOrProxyRelays(
followList.minus(blockedRelays)
}.onStart {
emit(outboxRelayFlow.value.minus(blockedRelayList.flow.value.toSet()))
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -94,7 +94,7 @@ class FollowListOutboxOrProxyRelays(
outboxRelayMinusBlockedFlow.value
},
)
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -108,7 +108,7 @@ class FollowListOutboxOrProxyRelays(
relayList.mapTo(mutableSetOf()) { it.url }
}.onStart {
emit(flow.value.mapTo(mutableSetOf()) { it.url })
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -42,7 +42,7 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.transformLatest
class FollowListReusedOutboxOrProxyRelays(
kind3Follows: FollowListState,
kind3Follows: Kind3FollowListState,
blockedRelayList: BlockedRelayListState,
proxyRelayList: ProxyRelayListState,
val cache: LocalCache,
@@ -78,7 +78,7 @@ class FollowListReusedOutboxOrProxyRelays(
},
)
}.distinctUntilChanged()
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -90,7 +90,7 @@ class FollowListReusedOutboxOrProxyRelays(
followList.minus(blockedRelays)
}.onStart {
emit(outboxRelayFlow.value.minus(blockedRelayList.flow.value.toSet()))
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -112,7 +112,7 @@ class FollowListReusedOutboxOrProxyRelays(
outboxRelayMinusBlockedFlow.value
},
)
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -126,7 +126,7 @@ class FollowListReusedOutboxOrProxyRelays(
relayList.mapTo(mutableSetOf()) { it.url }
}.onStart {
emit(flow.value.mapTo(mutableSetOf()) { it.url })
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -42,7 +42,7 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.transformLatest
class FollowsPerOutboxRelay(
kind3Follows: FollowListState,
kind3Follows: Kind3FollowListState,
blockedRelayList: BlockedRelayListState,
proxyRelayList: ProxyRelayListState,
val cache: LocalCache,
@@ -60,7 +60,7 @@ class FollowsPerOutboxRelay(
OutboxRelayLoader().authorsPerRelaySnapshot(kind3Follows.flow.value.authors, cache) { it },
)
}.distinctUntilChanged()
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -72,7 +72,7 @@ class FollowsPerOutboxRelay(
followList.minus(blockedRelays)
}.onStart {
emit(outboxPerRelayFlow.value.minus(blockedRelayList.flow.value.toSet()))
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -96,7 +96,7 @@ class FollowsPerOutboxRelay(
} else {
emit(proxyRelayList.flow.value.associateWith { kind3Follows.flow.value.authors })
}
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -25,12 +25,10 @@ import com.vitorpamplona.amethyst.model.AccountSettings
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.model.UserState
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.tags.geohash.geohashes
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
import com.vitorpamplona.quartz.nip02FollowList.tags.ContactTag
import com.vitorpamplona.quartz.nip73ExternalIds.location.GeohashId
import com.vitorpamplona.quartz.nip73ExternalIds.topics.HashtagId
import com.vitorpamplona.quartz.utils.Log
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
@@ -45,7 +43,7 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.transformLatest
import kotlinx.coroutines.launch
class FollowListState(
class Kind3FollowListState(
val signer: NostrSigner,
val cache: LocalCache,
val scope: CoroutineScope,
@@ -66,7 +64,7 @@ class FollowListState(
val flow =
innerFlow
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -77,11 +75,11 @@ class FollowListState(
// Creates a long-term reference for all follows of a user
val userList =
flow
.map {
it.authors.mapNotNull {
.map { kind3Follows ->
kind3Follows.authors.mapNotNull {
cache.checkGetOrCreateUser(it)
}
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -96,15 +94,9 @@ class FollowListState(
*/
@Immutable
class Kind3Follows(
val authors: Set<String> = emptySet(),
val authorsPlusMe: Set<String>,
val hashtags: Set<String> = emptySet(),
val geotags: Set<String> = emptySet(),
val communities: Set<String> = emptySet(),
) {
val geotagScopes: Set<String> = geotags.mapTo(mutableSetOf<String>()) { GeohashId.toScope(it) }
val hashtagScopes: Set<String> = hashtags.mapTo(mutableSetOf<String>()) { HashtagId.toScope(it) }
}
val authors: Set<HexKey> = emptySet(),
val authorsPlusMe: Set<HexKey>,
)
fun buildKind3Follows(latestContactList: ContactListEvent?): Kind3Follows {
// makes sure the output include only valid p tags
@@ -113,19 +105,6 @@ class FollowListState(
return Kind3Follows(
authors = verifiedFollowingUsers,
authorsPlusMe = verifiedFollowingUsers + signer.pubKey,
hashtags =
latestContactList
?.unverifiedFollowTagSet()
?.map { it.lowercase() }
?.toSet() ?: emptySet(),
geotags =
latestContactList
?.geohashes()
?.toSet() ?: emptySet(),
communities =
latestContactList
?.verifiedFollowAddressSet()
?.toSet() ?: emptySet(),
)
}
@@ -166,7 +145,7 @@ class FollowListState(
}
// saves contact list for the next time.
scope.launch(Dispatchers.Default) {
scope.launch(Dispatchers.IO) {
Log.d("AccountRegisterObservers", "Kind 3 Collector Start")
getFollowListFlow().collect {
Log.d("AccountRegisterObservers", "Updating Kind 3 ${signer.pubKey}")
@@ -42,7 +42,7 @@ class IncomingOtsEventVerifier(
newNotes.forEach {
consume(it)
}
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -47,7 +47,7 @@ class DmInboxRelayState(
localRelayList.flow,
) { nip65Inbox, dmRelayList, privateOutBox, localRelays ->
nip65Inbox + dmRelayList + privateOutBox + localRelays
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -64,7 +64,7 @@ class DmRelayListState(
getDMRelayListFlow()
.map { normalizeDMRelayListWithBackup(it.note) }
.onStart { emit(normalizeDMRelayListWithBackup(dmListNote)) }
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -96,7 +96,7 @@ class DmRelayListState(
}
}
scope.launch(Dispatchers.Default) {
scope.launch(Dispatchers.IO) {
Log.d("AccountRegisterObservers", "NIP-17 Relay List Collector Start")
getDMRelayListFlow().collect {
Log.d("AccountRegisterObservers", "Updating DM Relay List for ${signer.pubKey}")
@@ -71,7 +71,7 @@ class PublicChatListState(
emit(publicChatListWithBackup(noteState.note))
}.onStart {
emit(publicChatListWithBackup(publicChatListNote))
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -85,7 +85,7 @@ class PublicChatListState(
it.mapTo(mutableSetOf()) { it.eventId }
}.onStart {
emit(flow.value.mapTo(mutableSetOf()) { it.eventId })
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -132,7 +132,7 @@ class PublicChatListState(
}
}
scope.launch(Dispatchers.Default) {
scope.launch(Dispatchers.IO) {
Log.d("AccountRegisterObservers", "Channel List Collector Start")
getChannelListFlow().collect {
Log.d("AccountRegisterObservers", "Channel List for ${signer.pubKey}")
@@ -75,7 +75,7 @@ class EmojiPackState(
emit(convertEmojiSelectionPack(it.note.event as? EmojiPackSelectionEvent))
}.onStart {
emit(convertEmojiSelectionPack(getEmojiPackSelection()))
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -120,7 +120,7 @@ class EmojiPackState(
)?.map { it.value }?.toTypedArray() ?: emptyArray(),
),
)
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -76,7 +76,7 @@ class NwcSignerState(
settings.zapPaymentRequest
.map {
buildSigner(it) ?: signer
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -91,7 +91,7 @@ class NwcSignerState(
nip47Signer
.map {
NostrWalletConnectRequestCache(it)
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(scope, SharingStarted.Eagerly, NostrWalletConnectRequestCache(nip47Signer.value))
/**
@@ -102,7 +102,7 @@ class NwcSignerState(
nip47Signer
.map {
NostrWalletConnectResponseCache(it)
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(scope, SharingStarted.Eagerly, NostrWalletConnectResponseCache(nip47Signer.value))
fun buildSigner(uri: Nip47WalletConnect.Nip47URINorm?) =
@@ -78,7 +78,7 @@ class BookmarkListState(
}.onStart {
emit(publicBookmarks(bookmarkList))
}.debounce(100)
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -93,7 +93,7 @@ class BookmarkListState(
}.onStart {
emit(privateBookmarks(bookmarkList))
}.debounce(100)
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -107,7 +107,7 @@ class BookmarkListState(
.mapNotNull {
if (it is EventBookmark) it.eventId else null
}.toSet()
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -121,7 +121,7 @@ class BookmarkListState(
.mapNotNull {
if (it is AddressBookmark) it.address else null
}.toSet()
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -135,7 +135,7 @@ class BookmarkListState(
.mapNotNull {
if (it is EventBookmark) it.eventId else null
}.toSet()
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -149,7 +149,7 @@ class BookmarkListState(
.mapNotNull {
if (it is AddressBookmark) it.address else null
}.toSet()
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -185,7 +185,7 @@ class BookmarkListState(
emit(bookmarkList(private, public))
}.onStart {
emit(bookmarkList(privateBookmarks.value, publicBookmarks.value))
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -93,7 +93,7 @@ class HiddenUsersState(
settings.syncedSettings.security.showSensitiveContent.value,
),
)
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -61,7 +61,7 @@ class BlockPeopleListState(
getBlockListFlow()
.map { blockListWithBackup(it.note) }
.onStart { emit(blockListWithBackup(blockListNote)) }
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -67,7 +67,7 @@ class BlockedRelayListState(
.map {
normalizeBlockedRelayListWithBackup(it.note)
}.onStart { emit(normalizeBlockedRelayListWithBackup(blockedListNote)) }
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -99,7 +99,7 @@ class BlockedRelayListState(
GlobalScope.launch(Dispatchers.IO) { LocalCache.justConsumeMyOwnEvent(it) }
}
scope.launch(Dispatchers.Default) {
scope.launch(Dispatchers.IO) {
Log.d("AccountRegisterObservers", "Blocked Relay List Collector Start")
getBlockedRelayListFlow().collect {
Log.d("AccountRegisterObservers", "Updating Blocked Relay List for ${signer.pubKey}")
@@ -61,7 +61,7 @@ class BroadcastRelayListState(
getBroadcastRelayListFlow()
.map { normalizeBroadcastRelayListWithBackup(it.note) }
.onStart { emit(normalizeBroadcastRelayListWithBackup(broadcastListNote)) }
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -72,5 +72,5 @@ class GeohashListDecryptionCache(
emit(EmptyGeohashListCard)
}
}.distinctUntilChanged()
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
}
@@ -58,7 +58,7 @@ class GeohashListState(
suspend fun geohashListWithBackup(note: Note): Set<String> {
val event = note.event as? GeohashListEvent ?: settings.backupGeohashList
return event?.let { decryptionCache.geohashes(it) } ?: emptySet()
return event?.let { decryptionCache.geohashes(it).mapTo(mutableSetOf()) { it.lowercase() } } ?: emptySet()
}
@OptIn(ExperimentalCoroutinesApi::class)
@@ -68,7 +68,7 @@ class GeohashListState(
emit(geohashListWithBackup(noteState.note))
}.onStart {
emit(geohashListWithBackup(geohashListNote))
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -114,7 +114,7 @@ class GeohashListState(
}
}
scope.launch(Dispatchers.Default) {
scope.launch(Dispatchers.IO) {
Log.d("AccountRegisterObservers", "Geohash List Collector Start")
getGeohashListFlow().collect { noteState ->
Log.d("AccountRegisterObservers", "Geohash List for ${signer.pubKey}")
@@ -58,7 +58,7 @@ class HashtagListState(
suspend fun hashtagListWithBackup(note: Note): Set<String> {
val event = note.event as? HashtagListEvent ?: settings.backupHashtagList
return event?.let { decryptionCache.hashtags(it) } ?: emptySet()
return event?.let { decryptionCache.hashtags(it).mapTo(mutableSetOf()) { it.lowercase() } } ?: emptySet()
}
@OptIn(ExperimentalCoroutinesApi::class)
@@ -68,10 +68,10 @@ class HashtagListState(
emit(hashtagListWithBackup(noteState.note))
}.onStart {
emit(hashtagListWithBackup(hashtagListNote))
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Companion.Eagerly,
SharingStarted.Eagerly,
emptySet(),
)
@@ -79,9 +79,9 @@ class HashtagListState(
val hashtagList = getHashtagList()
return if (hashtagList == null) {
HashtagListEvent.Companion.create(hashtags, true, signer)
HashtagListEvent.create(hashtags, true, signer)
} else {
HashtagListEvent.Companion.add(hashtagList, hashtags, true, signer)
HashtagListEvent.add(hashtagList, hashtags, true, signer)
}
}
@@ -89,9 +89,9 @@ class HashtagListState(
val hashtagList = getHashtagList()
return if (hashtagList == null) {
HashtagListEvent.Companion.create(hashtag, true, signer)
HashtagListEvent.create(hashtag, true, signer)
} else {
HashtagListEvent.Companion.add(hashtagList, hashtag, true, signer)
HashtagListEvent.add(hashtagList, hashtag, true, signer)
}
}
@@ -99,7 +99,7 @@ class HashtagListState(
val hashtagList = getHashtagList()
return if (hashtagList != null) {
HashtagListEvent.Companion.remove(hashtagList, hashtag, signer)
HashtagListEvent.remove(hashtagList, hashtag, signer)
} else {
null
}
@@ -114,7 +114,7 @@ class HashtagListState(
}
}
scope.launch(Dispatchers.Default) {
scope.launch(Dispatchers.IO) {
Log.d("AccountRegisterObservers", "Hashtag List Collector Start")
getHashtagListFlow().collect {
Log.d("AccountRegisterObservers", "Hashtag List for ${signer.pubKey}")
@@ -67,7 +67,7 @@ class IndexerRelayListState(
getIndexerRelayListFlow()
.map { normalizeIndexerRelayListWithBackup(it.note) }
.onStart { emit(normalizeIndexerRelayListWithBackup(indexerListNote)) }
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -78,7 +78,7 @@ class IndexerRelayListState(
getIndexerRelayListFlow()
.map { normalizeIndexerRelayListWithBackupNoDefaults(it.note) }
.onStart { emit(normalizeIndexerRelayListWithBackupNoDefaults(indexerListNote)) }
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -109,7 +109,7 @@ class IndexerRelayListState(
GlobalScope.launch(Dispatchers.IO) { LocalCache.justConsumeMyOwnEvent(it) }
}
scope.launch(Dispatchers.Default) {
scope.launch(Dispatchers.IO) {
Log.d("AccountRegisterObservers", "Index Relay List Collector Start")
getIndexerRelayListFlow().collect {
Log.d("AccountRegisterObservers", "Updating Index Relay List for ${signer.pubKey}")
@@ -67,7 +67,7 @@ class MuteListState(
getMuteListFlow()
.map { muteListWithBackup(it.note) }
.onStart { emit(muteListWithBackup(muteListNote)) }
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -149,7 +149,7 @@ class MuteListState(
}
}
scope.launch(Dispatchers.Default) {
scope.launch(Dispatchers.IO) {
Log.d("AccountRegisterObservers", "Mute List Collector Start")
getMuteListFlow().collect {
Log.d("AccountRegisterObservers", "Updating Mute List for ${signer.pubKey}")
@@ -61,7 +61,7 @@ class ProxyRelayListState(
getProxyRelayListFlow()
.map { normalizeProxyRelayListWithBackup(it.note) }
.onStart { emit(normalizeProxyRelayListWithBackup(proxyListNote)) }
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -72,5 +72,5 @@ open class GenericRelayListCache<T : PrivateTagArrayEvent>(
emit(EmptyRelayListCard)
}
}.distinctUntilChanged()
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
}
@@ -67,7 +67,7 @@ class SearchRelayListState(
getSearchRelayListFlow()
.map { normalizeSearchRelayListWithBackup(it.note) }
.onStart { emit(normalizeSearchRelayListWithBackup(searchListNote)) }
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Companion.Eagerly,
@@ -78,7 +78,7 @@ class SearchRelayListState(
getSearchRelayListFlow()
.map { normalizeSearchRelayListWithBackupNoDefaults(it.note) }
.onStart { emit(normalizeSearchRelayListWithBackupNoDefaults(searchListNote)) }
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -109,7 +109,7 @@ class SearchRelayListState(
GlobalScope.launch(Dispatchers.IO) { LocalCache.justConsumeMyOwnEvent(it) }
}
scope.launch(Dispatchers.Default) {
scope.launch(Dispatchers.IO) {
Log.d("AccountRegisterObservers", "Search Relay List Collector Start")
getSearchRelayListFlow().collect {
Log.d("AccountRegisterObservers", "Updating Search Relay List for ${signer.pubKey}")
@@ -65,7 +65,7 @@ class TrustedRelayListState(
getTrustedRelayListFlow()
.map { normalizeTrustedRelayListWithBackup(it.note) }
.onStart { emit(normalizeTrustedRelayListWithBackup(trustedListNote)) }
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Companion.Eagerly,
@@ -96,7 +96,7 @@ class TrustedRelayListState(
GlobalScope.launch(Dispatchers.IO) { cache.justConsumeMyOwnEvent(it) }
}
scope.launch(Dispatchers.Default) {
scope.launch(Dispatchers.IO) {
Log.d("AccountRegisterObservers", "Trusted Relay List Collector Start")
getTrustedRelayListFlow().collect {
Log.d("AccountRegisterObservers", "Updating Trusted Relay List for ${signer.pubKey}")
@@ -71,7 +71,7 @@ class Nip65RelayListState(
getNIP65RelayListFlow()
.map { normalizeNIP65WriteRelayListWithBackup(it.note) }
.onStart { emit(normalizeNIP65ReadRelayListWithBackup(nip65ListNote)) }
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -82,7 +82,7 @@ class Nip65RelayListState(
getNIP65RelayListFlow()
.map { normalizeNIP65ReadRelayListWithBackup(it.note) }
.onStart { emit(normalizeNIP65ReadRelayListWithBackup(nip65ListNote)) }
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -93,7 +93,7 @@ class Nip65RelayListState(
getNIP65RelayListFlow()
.map { normalizeNIP65AllRelayListWithBackupNoDefaults(it.note) }
.onStart { emit(normalizeNIP65AllRelayListWithBackupNoDefaults(nip65ListNote)) }
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -124,7 +124,7 @@ class Nip65RelayListState(
GlobalScope.launch(Dispatchers.IO) { cache.justConsumeMyOwnEvent(it) }
}
scope.launch(Dispatchers.Default) {
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}")
@@ -72,7 +72,7 @@ class CommunityListState(
emit(communityListWithBackup(noteState.note))
}.onStart {
emit(communityListWithBackup(communityListNote))
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -86,7 +86,7 @@ class CommunityListState(
hint.mapTo(mutableSetOf()) { it.address.toValue() }
}.onStart {
emit(flow.value.mapTo(mutableSetOf()) { it.address.toValue() })
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -147,7 +147,7 @@ class CommunityListState(
}
}
scope.launch(Dispatchers.Default) {
scope.launch(Dispatchers.IO) {
Log.d("AccountRegisterObservers", "Community List Collector Start")
getCommunityListFlow().collect {
Log.d("AccountRegisterObservers", "Community List for ${signer.pubKey}")
@@ -81,7 +81,7 @@ class AppSpecificState(
}
}
scope.launch(Dispatchers.Default) {
scope.launch(Dispatchers.IO) {
Log.d("AccountRegisterObservers", "AppSpecificData Collector Start")
getAppSpecificDataFlow().collect {
try {
@@ -59,7 +59,7 @@ class FileStorageServerListState(
getFileServersListFlow()
.map { normalizeServers(it.note) }
.onStart { emit(normalizeServers(fileStorageListNote)) }
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -61,7 +61,7 @@ class BlossomServerListState(
getBlossomServersListFlow()
.map { normalizeServers(it.note) }
.onStart { emit(normalizeServers(blossomListNote)) }
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -34,7 +34,7 @@ class UpdatablePropertyFlow<T>(
) {
val stateFlow =
flow
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(
scope = scope,
started = SharingStarted.Eagerly,
@@ -20,12 +20,15 @@
*/
package com.vitorpamplona.amethyst.model.serverList
import com.vitorpamplona.amethyst.model.nip02FollowLists.FollowListState
import androidx.compose.runtime.Immutable
import com.vitorpamplona.amethyst.model.nip02FollowLists.Kind3FollowListState
import com.vitorpamplona.amethyst.model.nip51Lists.followSets.FollowSetState
import com.vitorpamplona.amethyst.model.nip51Lists.geohashLists.GeohashListState
import com.vitorpamplona.amethyst.model.nip51Lists.hashtagLists.HashtagListState
import com.vitorpamplona.amethyst.model.nip72Communities.CommunityListState
import com.vitorpamplona.quartz.nip72ModCommunities.follow.tags.CommunityTag
import com.vitorpamplona.quartz.nip73ExternalIds.location.GeohashId
import com.vitorpamplona.quartz.nip73ExternalIds.topics.HashtagId
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.SharingStarted
@@ -37,38 +40,50 @@ import kotlinx.coroutines.flow.sample
import kotlinx.coroutines.flow.stateIn
class MergedFollowListsState(
val kind3List: FollowListState,
val kind3List: Kind3FollowListState,
val followSetList: FollowSetState,
val hashtagList: HashtagListState,
val geohashList: GeohashListState,
val communityList: CommunityListState,
val scope: CoroutineScope,
) {
/**
This contains a big OR of everything the user wants to see in the a single feed.
*/
@Immutable
class AllFollows(
val authors: Set<String> = emptySet(),
val hashtags: Set<String> = emptySet(),
val geotags: Set<String> = emptySet(),
val communities: Set<String> = emptySet(),
) {
val geotagScopes: Set<String> = geotags.mapTo(mutableSetOf<String>()) { GeohashId.toScope(it) }
val hashtagScopes: Set<String> = hashtags.mapTo(mutableSetOf<String>()) { HashtagId.toScope(it) }
}
fun mergeLists(
kind3: FollowListState.Kind3Follows,
kind3: Kind3FollowListState.Kind3Follows,
followSetProfiles: Set<String>,
hashtags: Set<String>,
geohashes: Set<String>,
community: Set<CommunityTag>,
): FollowListState.Kind3Follows =
FollowListState.Kind3Follows(
kind3.authors + followSetProfiles,
kind3.authorsPlusMe,
kind3.hashtags + hashtags,
kind3.geotags + geohashes,
kind3.communities + community.map { it.address.toValue() },
): AllFollows =
AllFollows(
authors = kind3.authors + followSetProfiles,
hashtags = hashtags,
geotags = geohashes,
communities = community.mapTo(mutableSetOf()) { it.address.toValue() },
)
val flow: StateFlow<FollowListState.Kind3Follows> =
val flow: StateFlow<AllFollows> =
combine(
kind3List.flow,
followSetList.profilesFlow,
hashtagList.flow,
geohashList.flow,
communityList.flow,
) { kind3, followSet, hashtag, geohash, community ->
mergeLists(kind3, followSet, hashtag, geohash, community)
}.onStart {
::mergeLists,
).onStart {
emit(
mergeLists(
kind3List.flow.value,
@@ -79,10 +94,16 @@ class MergedFollowListsState(
),
)
}.sample(200)
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
kind3List.flow.value,
mergeLists(
kind3List.flow.value,
followSetList.profilesFlow.value,
hashtagList.flow.value,
geohashList.flow.value,
communityList.flow.value,
),
)
}
@@ -65,7 +65,7 @@ class MergedFollowPlusMineRelayListsState(
),
),
)
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -69,7 +69,7 @@ class MergedFollowPlusMineWithIndexRelayListsState(
),
),
)
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -69,7 +69,7 @@ class MergedFollowPlusMineWithSearchRelayListsState(
),
),
)
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -64,7 +64,7 @@ class MergedServerListState(
mergeServerList(nip96s, blossoms)
}.onStart {
emit(mergeServerList(fileServers.flow.value, blossomServers.flow.value))
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -75,7 +75,7 @@ class TrustedRelayListsState(
),
),
)
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -25,7 +25,7 @@ import com.vitorpamplona.amethyst.model.ALL_USER_FOLLOWS
import com.vitorpamplona.amethyst.model.AROUND_ME
import com.vitorpamplona.amethyst.model.GLOBAL_FOLLOWS
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.nip02FollowLists.FollowListState
import com.vitorpamplona.amethyst.model.serverList.MergedFollowListsState
import com.vitorpamplona.amethyst.model.topNavFeeds.allFollows.AllFollowsFeedFlow
import com.vitorpamplona.amethyst.model.topNavFeeds.allUserFollows.AllUserFollowsFeedFlow
import com.vitorpamplona.amethyst.model.topNavFeeds.aroundMe.AroundMeFeedFlow
@@ -49,7 +49,7 @@ import kotlinx.coroutines.flow.transformLatest
class FeedTopNavFilterState(
val feedFilterListName: MutableStateFlow<String>,
val allFollows: StateFlow<FollowListState.Kind3Follows>,
val allFollows: StateFlow<MergedFollowListsState.AllFollows>,
val locationFlow: StateFlow<LocationState.LocationResult>,
val followsRelays: StateFlow<Set<NormalizedRelayUrl>>,
val blockedRelays: StateFlow<Set<NormalizedRelayUrl>>,
@@ -81,7 +81,7 @@ class FeedTopNavFilterState(
emitAll(loadFlowsFor(listName).flow())
}.onStart {
loadFlowsFor(feedFilterListName.value).startValue(this)
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -100,7 +100,7 @@ class MergedTopFeedAuthorListsState(
notificationNavFilter.value,
),
)
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -45,7 +45,7 @@ class OutboxLoaderState(
emitAll(filterSettings.toPerRelayFlow(cache))
}.onStart {
emit(topNavFilter.value.startValue(cache))
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Companion.Eagerly,
@@ -20,7 +20,7 @@
*/
package com.vitorpamplona.amethyst.model.topNavFeeds.allFollows
import com.vitorpamplona.amethyst.model.nip02FollowLists.FollowListState
import com.vitorpamplona.amethyst.model.serverList.MergedFollowListsState
import com.vitorpamplona.amethyst.model.topNavFeeds.IFeedFlowsType
import com.vitorpamplona.amethyst.model.topNavFeeds.IFeedTopNavFilter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
@@ -29,31 +29,31 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
class AllFollowsFeedFlow(
val allFollows: StateFlow<FollowListState.Kind3Follows?>,
val allFollows: StateFlow<MergedFollowListsState.AllFollows?>,
val followsRelays: StateFlow<Set<NormalizedRelayUrl>>,
val blockedRelays: StateFlow<Set<NormalizedRelayUrl>>,
val proxyRelays: StateFlow<Set<NormalizedRelayUrl>>,
) : IFeedFlowsType {
fun convert(
kind3: FollowListState.Kind3Follows?,
allFollows: MergedFollowListsState.AllFollows?,
proxyRelays: Set<NormalizedRelayUrl>,
): IFeedTopNavFilter =
if (kind3 != null) {
if (allFollows != null) {
if (proxyRelays.isEmpty()) {
AllFollowsByOutboxTopNavFilter(
authors = kind3.authors,
hashtags = kind3.hashtags,
geotags = kind3.geotags,
communities = kind3.communities,
authors = allFollows.authors,
hashtags = allFollows.hashtags,
geotags = allFollows.geotags,
communities = allFollows.communities,
defaultRelays = followsRelays,
blockedRelays = blockedRelays,
)
} else {
AllFollowsByProxyTopNavFilter(
authors = kind3.authors,
hashtags = kind3.hashtags,
geotags = kind3.geotags,
communities = kind3.communities,
authors = allFollows.authors,
hashtags = allFollows.hashtags,
geotags = allFollows.geotags,
communities = allFollows.communities,
proxyRelays = proxyRelays,
)
}
@@ -20,7 +20,7 @@
*/
package com.vitorpamplona.amethyst.model.topNavFeeds.allUserFollows
import com.vitorpamplona.amethyst.model.nip02FollowLists.FollowListState
import com.vitorpamplona.amethyst.model.serverList.MergedFollowListsState
import com.vitorpamplona.amethyst.model.topNavFeeds.IFeedFlowsType
import com.vitorpamplona.amethyst.model.topNavFeeds.IFeedTopNavFilter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
@@ -29,25 +29,25 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
class AllUserFollowsFeedFlow(
val allFollows: StateFlow<FollowListState.Kind3Follows?>,
val allFollows: StateFlow<MergedFollowListsState.AllFollows?>,
val followsRelays: StateFlow<Set<NormalizedRelayUrl>>,
val blockedRelays: StateFlow<Set<NormalizedRelayUrl>>,
val proxyRelays: StateFlow<Set<NormalizedRelayUrl>>,
) : IFeedFlowsType {
fun convert(
kind3: FollowListState.Kind3Follows?,
allFollows: MergedFollowListsState.AllFollows?,
proxyRelays: Set<NormalizedRelayUrl>,
): IFeedTopNavFilter =
if (kind3 != null) {
if (allFollows != null) {
if (proxyRelays.isEmpty()) {
AllUserFollowsByOutboxTopNavFilter(
authors = kind3.authors,
authors = allFollows.authors,
defaultRelays = followsRelays,
blockedRelays = blockedRelays,
)
} else {
AllUserFollowsByProxyTopNavFilter(
authors = kind3.authors,
authors = allFollows.authors,
proxyRelays = proxyRelays,
)
}
@@ -74,7 +74,7 @@ class TorRelayState(
trustedRelaysViaTor = torSettingsFlow.trustedRelaysViaTor.value,
),
)
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -108,7 +108,7 @@ class TorRelayState(
dmRelayList = dmRelays.value,
),
)
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
scope,
SharingStarted.Eagerly,
@@ -47,7 +47,7 @@ class PokeyReceiver : BroadcastReceiver() {
Log.e("AmethystCoroutine", "Caught exception: ${throwable.message}", throwable)
}
val scope = CoroutineScope(Dispatchers.Default + SupervisorJob() + exceptionHandler)
val scope = CoroutineScope(Dispatchers.IO + SupervisorJob() + exceptionHandler)
fun register(app: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
@@ -76,7 +76,7 @@ class OkHttpWebSocket(
inner class OkHttpWebsocketListener(
val out: WebSocketListener,
) : okhttp3.WebSocketListener() {
val scope = CoroutineScope(Dispatchers.Default + exceptionHandler)
val scope = CoroutineScope(Dispatchers.IO + exceptionHandler)
val incomingMessages: Channel<String> = Channel(Channel.UNLIMITED)
val job = // Launch a coroutine to process messages from the channel.
scope.launch {
@@ -65,7 +65,7 @@ fun MuteButton(
}
LaunchedEffect(key1 = controllerVisible) {
launch(Dispatchers.Default) {
launch(Dispatchers.IO) {
delay(2000)
holdOn.value = false
}
@@ -55,7 +55,7 @@ abstract class BaseEoseManager<T>(
fun dismissSubscription(subId: String) = orchestrator.dismissSubscription(subId)
// Refreshes observers in batches.
private val bundler = BundledUpdate(sampleTime, Dispatchers.Default)
private val bundler = BundledUpdate(sampleTime, Dispatchers.IO)
override fun invalidateFilters(ignoreIfDoing: Boolean) {
bundler.invalidate(ignoreIfDoing, ::forceInvalidate)
@@ -63,7 +63,7 @@ class AccountDraftsEoseManager(
userJobMap[user]?.forEach { it.cancel() }
userJobMap[user] =
listOf(
key.account.scope.launch(Dispatchers.Default) {
key.account.scope.launch(Dispatchers.IO) {
relayFlow(key).collectLatest {
invalidateFilters()
}
@@ -67,7 +67,7 @@ class AccountFollowsLoaderSubAssembler(
private val orchestrator = SubscriptionController(client)
// Refreshes observers in batches of 500ms
private val bundler = BundledUpdate(500, Dispatchers.Default)
private val bundler = BundledUpdate(500, Dispatchers.IO)
/**
* This assembler saves the EOSE per user key. That EOSE includes their metadata, etc
@@ -203,7 +203,7 @@ class AccountFollowsLoaderSubAssembler(
) {
accountUpdatesJobMap[user]?.cancel()
accountUpdatesJobMap[user] =
scope.launch(Dispatchers.Default) {
scope.launch(Dispatchers.IO) {
followList.sample(1000).collectLatest {
invalidateFilters(true)
}
@@ -65,7 +65,7 @@ class AccountMetadataEoseManager(
userJobMap[user]?.forEach { it.cancel() }
userJobMap[user] =
listOf(
key.account.scope.launch(Dispatchers.Default) {
key.account.scope.launch(Dispatchers.IO) {
relayFlow(key).collectLatest {
invalidateFilters()
}
@@ -71,12 +71,12 @@ class AccountNotificationsEoseFromInboxRelaysManager(
userJobMap[user]?.forEach { it.cancel() }
userJobMap[user] =
listOf(
key.account.scope.launch(Dispatchers.Default) {
key.account.scope.launch(Dispatchers.IO) {
key.account.notificationRelays.flow.sample(1000).collectLatest {
invalidateFilters()
}
},
key.account.scope.launch(Dispatchers.Default) {
key.account.scope.launch(Dispatchers.IO) {
key.feedContentStates.notifications.lastNoteCreatedAtWhenFullyLoaded.sample(5000).collectLatest {
invalidateFilters()
}
@@ -67,13 +67,13 @@ class AccountNotificationsEoseFromRandomRelaysManager(
userJobMap[user]?.forEach { it.cancel() }
userJobMap[user] =
listOf(
key.account.scope.launch(Dispatchers.Default) {
key.account.scope.launch(Dispatchers.IO) {
// no need to hurry here. we can wait the app stabilize
key.account.followsPerRelay.debounce(5000).collectLatest {
invalidateFilters()
}
},
key.account.scope.launch(Dispatchers.Default) {
key.account.scope.launch(Dispatchers.IO) {
key.feedContentStates.notifications.lastNoteCreatedAtWhenFullyLoaded.sample(5000).collectLatest {
invalidateFilters()
}
@@ -65,7 +65,7 @@ class AccountGiftWrapsEoseManager(
userJobMap[user]?.forEach { it.cancel() }
userJobMap[user] =
listOf(
key.account.scope.launch(Dispatchers.Default) {
key.account.scope.launch(Dispatchers.IO) {
key.account.dmRelays.flow.collectLatest {
invalidateFilters()
}
@@ -76,7 +76,7 @@ fun observeChannelNoteAuthors(
channelToParticipatingUsers(baseChannel, accountViewModel),
)
}.distinctUntilChanged()
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
}
return flow.collectAsStateWithLifecycle(persistentListOf())
@@ -97,7 +97,7 @@ fun <T> observeNoteAndMap(
.metadata.stateFlow
.mapLatest { map(it.note) }
.distinctUntilChanged()
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
}
// Subscribe in the LocalCache for changes that arrive in the device
@@ -123,7 +123,7 @@ fun <T, U> observeNoteEventAndMap(
.metadata.stateFlow
.mapLatest { (it.note.event as? T)?.let { map(it) } }
.distinctUntilChanged()
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
}
// Subscribe in the LocalCache for changes that arrive in the device
@@ -223,7 +223,7 @@ fun observeNoteReactionCount(
.sample(200)
.mapLatest { it.note.countReactions() }
.distinctUntilChanged()
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
}
// Subscribe in the LocalCache for changes that arrive in the device
@@ -278,7 +278,7 @@ fun observeNoteRepostsBy(
.boosts.stateFlow
.mapLatest { it.note.isBoostedBy(user) }
.distinctUntilChanged()
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
}
return flow.collectAsStateWithLifecycle(note.isBoostedBy(user))
@@ -401,7 +401,7 @@ fun observeCommunityApprovalNeedStatus(
}
}
}.distinctUntilChanged()
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
}
// Subscribe in the LocalCache for changes that arrive in the device
@@ -32,6 +32,10 @@ import com.vitorpamplona.quartz.utils.mapOfSet
fun potentialRelaysToFindAddress(note: AddressableNote): Set<NormalizedRelayUrl> {
val set = mutableSetOf<NormalizedRelayUrl>()
LocalCache.getOrCreateUser(note.address.pubKeyHex).outboxRelays()?.let {
set.addAll(it)
}
set.addAll(LocalCache.relayHints.hintsForAddress(note.idHex))
LocalCache.getAnyChannel(note)?.relays()?.let { set.addAll(it) }
@@ -253,7 +253,7 @@ fun observeUserFollowCount(
.mapLatest { userState ->
userState.user.transientFollowCount() ?: 0
}.distinctUntilChanged()
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
}
return flow.collectAsStateWithLifecycle(0)
@@ -281,7 +281,7 @@ fun observeUserTagFollowCount(
}.onStart {
emit((accountViewModel.hashtagFollows(user).event as? HashtagListEvent)?.let { accountViewModel.account.hashtagListDecryptionCache.hashtags(it) }?.size ?: 0)
}.distinctUntilChanged()
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
}
return flow.collectAsStateWithLifecycle(0)
@@ -309,7 +309,7 @@ fun observeUserTagFollows(
}.onStart {
emit((accountViewModel.hashtagFollows(user).event as? HashtagListEvent)?.let { accountViewModel.account.hashtagListDecryptionCache.hashtags(it) }?.sorted() ?: emptyList())
}.distinctUntilChanged()
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
}
return flow.collectAsStateWithLifecycle(emptyList())
@@ -356,7 +356,7 @@ fun observeUserBookmarkCount(
.mapLatest { noteState ->
(noteState.note.event as? BookmarkListEvent)?.countBookmarks() ?: 0
}.distinctUntilChanged()
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
}
return flow.collectAsStateWithLifecycle(0)
@@ -396,7 +396,7 @@ fun observeUserFollowerCount(
.mapLatest { userState ->
userState.user.transientFollowerCount()
}.distinctUntilChanged()
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
}
return flow.collectAsStateWithLifecycle(0)
@@ -426,7 +426,7 @@ fun observeUserIsFollowing(
.mapLatest { userState ->
userState.user.isFollowing(user2) || isUserInFollowSets
}.distinctUntilChanged()
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
}
return flow.collectAsStateWithLifecycle(
@@ -450,7 +450,7 @@ fun observeUserIsFollowingHashtag(
}.onStart {
emit(hashtag in accountViewModel.account.hashtagList.flow.value)
}.distinctUntilChanged()
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
}
return flow.collectAsStateWithLifecycle(hashtag in accountViewModel.account.hashtagList.flow.value)
@@ -471,7 +471,7 @@ fun observeUserIsFollowingGeohash(
}.onStart {
emit(geohash in accountViewModel.account.geohashList.flow.value)
}.distinctUntilChanged()
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
}
return flow.collectAsStateWithLifecycle(geohash in accountViewModel.account.geohashList.flow.value)
@@ -496,7 +496,7 @@ fun observeUserIsFollowingChannel(
.mapLatest { followingChannels ->
channel.idHex in followingChannels
}.distinctUntilChanged()
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
}
@SuppressLint("StateFlowValueCalledInComposition")
@@ -522,7 +522,7 @@ fun observeUserIsFollowingChannel(
.mapLatest { followingChannels ->
channel.roomId in followingChannels
}.distinctUntilChanged()
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
}
@SuppressLint("StateFlowValueCalledInComposition")
@@ -563,7 +563,7 @@ fun observeUserZapAmount(
.mapLatest { userState ->
userState.user.zappedAmount()
}.distinctUntilChanged()
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
}
return flow.collectAsStateWithLifecycle(BigDecimal.ZERO)
@@ -630,7 +630,7 @@ fun observeUserStatuses(
.mapLatest { userState ->
LocalCache.findStatusesForUser(userState.user)
}.distinctUntilChanged()
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
}
return flow.collectAsStateWithLifecycle(persistentListOf())
@@ -649,7 +649,7 @@ fun observeUserRelayIntoList(
.mapLatest { relays ->
relayUrl in relays
}.distinctUntilChanged()
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
}
return flow.collectAsStateWithLifecycle(false)
@@ -72,7 +72,7 @@ fun filterUserMetadataForKey(
users.forEach { user ->
val time = since.since(user)?.get(relay)?.time
if (time == null) {
if (time == null || user.latestMetadata == null) {
firstTimers.add(user.pubkeyHex)
} else {
updates.add(user.pubkeyHex)
@@ -82,24 +82,32 @@ fun filterUserMetadataForKey(
}
}
listOf(
RelayBasedFilter(
relay = relay,
filter =
Filter(
kinds = UserMetadataForKeyKinds,
authors = firstTimers.sorted(),
),
),
RelayBasedFilter(
relay = relay,
filter =
Filter(
kinds = UserMetadataForKeyKinds,
authors = updates.sorted(),
since = minimumTime,
),
),
listOfNotNull(
if (firstTimers.isNotEmpty()) {
RelayBasedFilter(
relay = relay,
filter =
Filter(
kinds = UserMetadataForKeyKinds,
authors = firstTimers.sorted(),
),
)
} else {
null
},
if (updates.isNotEmpty()) {
RelayBasedFilter(
relay = relay,
filter =
Filter(
kinds = UserMetadataForKeyKinds,
authors = updates.sorted(),
since = minimumTime,
),
)
} else {
null
},
)
}.flatten()
}
@@ -69,7 +69,7 @@ class UserReportsSubAssembler(
val trustedAccounts =
mapOfSet {
accounts.map { it.followsPerRelay.value }.forEach {
accounts.map { it.declaredFollowsPerRelay.value }.forEach {
add(it)
}
}
@@ -110,7 +110,7 @@ class MainActivity : AppCompatActivity() {
// Graph doesn't completely clear.
// @OptIn(DelicateCoroutinesApi::class)
// GlobalScope.launch(Dispatchers.Default) {
// GlobalScope.launch(Dispatchers.IO) {
// serviceManager.trimMemory()
// }
@@ -85,7 +85,7 @@ fun CashuPreview(
initialValue = CachedCashuParser.cached(cashutoken),
key1 = cashutoken,
) {
val newToken = withContext(Dispatchers.Default) { CachedCashuParser.parse(cashutoken) }
val newToken = withContext(Dispatchers.IO) { CachedCashuParser.parse(cashutoken) }
if (value != newToken) {
value = newToken
}
@@ -583,7 +583,7 @@ fun DisplaySecretEmoji(
if (secretContent == null) {
LaunchedEffect(segment) {
launch(Dispatchers.Default) {
launch(Dispatchers.IO) {
secretContent =
CachedRichTextParser.parseText(
EmojiCoder.decode(segment.segmentText),
@@ -150,7 +150,7 @@ private fun DialogContent(
pagerState.scrollToPage(page)
}
}
launch(Dispatchers.Default) {
launch(Dispatchers.IO) {
delay(2000)
withContext(Dispatchers.Main) {
controllerVisible.value = false
@@ -68,7 +68,7 @@ class ChannelFeedContentState(
}
private fun refresh() {
viewModelScope.launch(Dispatchers.Default) { refreshSuspended() }
viewModelScope.launch(Dispatchers.IO) { refreshSuspended() }
}
fun refreshSuspended() {
@@ -135,8 +135,8 @@ class ChannelFeedContentState(
}
}
private val bundler = BundledUpdate(250, Dispatchers.Default)
private val bundlerInsert = BundledInsert<Set<Note>>(250, Dispatchers.Default)
private val bundler = BundledUpdate(250, Dispatchers.IO)
private val bundlerInsert = BundledInsert<Set<Note>>(250, Dispatchers.IO)
override fun invalidateData(ignoreIfDoing: Boolean) {
viewModelScope.launch(Dispatchers.IO) {
@@ -71,7 +71,7 @@ class FeedContentState(
}
private fun refresh() {
viewModelScope.launch(Dispatchers.Default) { refreshSuspended() }
viewModelScope.launch(Dispatchers.IO) { refreshSuspended() }
}
fun visibleNotes(): List<Note> {
@@ -134,7 +134,7 @@ fun LoadOts(
LaunchedEffect(key1 = noteStatus) {
val newOts =
withContext(Dispatchers.Default) {
withContext(Dispatchers.IO) {
LocalCache.findEarliestOtsForNote(
note = noteStatus?.note ?: note,
otsVerifCache = Amethyst.instance.otsVerifCache,
@@ -260,7 +260,7 @@ fun DisplaySecretEmojiAsReaction(
if (secretContent == null) {
LaunchedEffect(reaction) {
launch(Dispatchers.Default) {
launch(Dispatchers.IO) {
secretContent =
CachedRichTextParser.parseText(
EmojiCoder.decode(reaction),
@@ -100,7 +100,7 @@ class PollNoteViewModel : ViewModel() {
}
fun refreshTallies() {
viewModelScope.launch(Dispatchers.Default) {
viewModelScope.launch(Dispatchers.IO) {
totalZapped = totalZapped()
wasZappedByLoggedInAccount = false
wasZappedByLoggedInAccount = account.calculateIfNoteWasZappedByAccount(pollNote, 0)
@@ -44,7 +44,7 @@ class EmojiSuggestionState(
} else {
emptyList()
}
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
fun reset() {
if (search.value.isNotEmpty()) {
@@ -72,7 +72,7 @@ fun LoadValueFromInvoice(
@Suppress("ProduceStateDoesNotAssignValue")
val lnInvoice by
produceState(initialValue = CachedLnInvoiceParser.cached(lnbcWord), key1 = lnbcWord) {
val newLnInvoice = withContext(Dispatchers.Default) { CachedLnInvoiceParser.parse(lnbcWord) }
val newLnInvoice = withContext(Dispatchers.IO) { CachedLnInvoiceParser.parse(lnbcWord) }
if (value != newLnInvoice) {
value = newLnInvoice
}
@@ -39,7 +39,7 @@ class PreviewState {
.debounce(500)
.map { CachedUrlParser.parseValidUrls(it.text) }
.distinctUntilChanged()
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
fun reset() {
source.tryEmit(TextFieldValue(""))
@@ -64,12 +64,12 @@ fun ShowUserSuggestionList(
AnimateOnNewSearch(userSuggestions, listState)
LaunchedEffect(Unit) {
launch(Dispatchers.Default) {
launch(Dispatchers.IO) {
LocalCache.live.newEventBundles.collect {
userSuggestions.invalidateData()
}
}
launch(Dispatchers.Default) {
launch(Dispatchers.IO) {
LocalCache.live.deletedEventBundles.collect {
userSuggestions.invalidateData()
}
@@ -62,7 +62,7 @@ class UserSuggestionState(
} else {
emptyList()
}
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
fun reset() {
currentWord.tryEmit("")
@@ -22,14 +22,12 @@ package com.vitorpamplona.amethyst.ui.note.elements
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.text.style.TextOverflow
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.ui.components.buildLinkString
import com.vitorpamplona.amethyst.ui.components.ClickableTextColor
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@@ -53,29 +51,20 @@ private fun DisplayCommunity(
note: Note,
nav: INav,
) {
val communityTag =
remember(note) { note.event?.communityAddress() } ?: return
val communityTag = note.event?.communityAddress() ?: return
val displayTag =
remember(note) {
buildLinkString(
getCommunityShortName(communityTag),
) {
nav.nav(Route.Community(communityTag.kind, communityTag.pubKeyHex, communityTag.dTag))
ClickableTextColor(
getCommunityShortName(communityTag),
linkColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.52f),
overflow = TextOverflow.Ellipsis,
maxLines = 1,
) {
nav.nav {
note.event?.communityAddress()?.let { communityTag ->
Route.Community(communityTag.kind, communityTag.pubKeyHex, communityTag.dTag)
}
}
Text(
text = displayTag,
style =
LocalTextStyle.current.copy(
color =
MaterialTheme.colorScheme.primary.copy(
alpha = 0.52f,
),
),
maxLines = 1,
)
}
}
fun getCommunityShortName(communityAddress: Address): String {
@@ -23,9 +23,7 @@ package com.vitorpamplona.amethyst.ui.note.elements
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -36,7 +34,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.text.style.TextOverflow
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.ui.components.buildLinkString
import com.vitorpamplona.amethyst.ui.components.ClickableTextColor
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@@ -82,15 +80,12 @@ private fun DisplayTagList(
accountViewModel: AccountViewModel,
nav: INav,
) {
Text(
text =
remember(firstTag) {
buildLinkString(" #$firstTag") {
nav.nav(Route.Hashtag(firstTag))
}
},
style = LocalTextStyle.current.copy(MaterialTheme.colorScheme.primary.copy(alpha = 0.52f)),
ClickableTextColor(
"#$firstTag",
linkColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.52f),
overflow = TextOverflow.Ellipsis,
maxLines = 1,
)
) {
nav.nav(Route.Hashtag(firstTag))
}
}
@@ -323,7 +323,7 @@ open class CommentPostViewModel :
cancel()
accountViewModel.account.signAndComputeBroadcast(template, extraNotesToBroadcast)
accountViewModel.viewModelScope.launch(Dispatchers.Default) {
accountViewModel.viewModelScope.launch(Dispatchers.IO) {
accountViewModel.account.deleteDraftIgnoreErrors(version)
}
}
@@ -465,7 +465,7 @@ open class CommentPostViewModel :
onError: (title: String, message: String) -> Unit,
context: Context,
) {
viewModelScope.launch(Dispatchers.Default) {
viewModelScope.launch(Dispatchers.IO) {
val myMultiOrchestrator = multiOrchestrator ?: return@launch
isUploadingImage = true
@@ -673,7 +673,7 @@ open class CommentPostViewModel :
}
override fun updateZapFromText() {
viewModelScope.launch(Dispatchers.Default) {
viewModelScope.launch(Dispatchers.IO) {
val tagger =
NewMessageTagger(message.text, emptyList(), emptyList(), accountViewModel)
tagger.run()
@@ -88,7 +88,7 @@ fun RenderAppDefinition(
var metadata by remember { mutableStateOf<AppMetadata?>(null) }
LaunchedEffect(key1 = noteEvent) {
withContext(Dispatchers.Default) { metadata = noteEvent.appMetaData() }
withContext(Dispatchers.IO) { metadata = noteEvent.appMetaData() }
}
metadata?.let { theAppMetadata ->
@@ -185,7 +185,7 @@ fun RenderFhirResource(
fun RenderFhirResource(event: FhirResourceEvent) {
@Suppress("ProduceStateDoesNotAssignValue")
val state by produceState(initialValue = FhirElementDatabase(), key1 = event) {
withContext(Dispatchers.Default) {
withContext(Dispatchers.IO) {
parseResourceBundleOrNull(event.content)?.let {
value = it
}
@@ -49,14 +49,14 @@ abstract class FeedViewModel(
init {
Log.d("Init", "Starting new Model: ${this.javaClass.simpleName}")
viewModelScope.launch(Dispatchers.Default) {
viewModelScope.launch(Dispatchers.IO) {
LocalCache.live.newEventBundles.collect { newNotes ->
Log.d("Rendering Metrics", "Update feeds: ${this@FeedViewModel.javaClass.simpleName} with ${newNotes.size}")
feedState.updateFeedWith(newNotes)
}
}
viewModelScope.launch(Dispatchers.Default) {
viewModelScope.launch(Dispatchers.IO) {
LocalCache.live.deletedEventBundles.collect { newNotes ->
Log.d("Rendering Metrics", "Delete from feeds: ${this@FeedViewModel.javaClass.simpleName} with ${newNotes.size}")
feedState.deleteFromFeed(newNotes)
@@ -253,11 +253,11 @@ class FollowListState(
val kind3GlobalPeopleRoutes =
_kind3GlobalPeopleRoutes
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(viewModelScope, SharingStarted.Eagerly, defaultLists)
val kind3GlobalPeople =
_kind3GlobalPeople
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(viewModelScope, SharingStarted.Eagerly, defaultLists)
suspend fun initializeSuspend() {
@@ -49,7 +49,7 @@ open class UserFeedViewModel(
val feedContent = _feedContent.asStateFlow()
private fun refresh() {
viewModelScope.launch(Dispatchers.Default) { refreshSuspended() }
viewModelScope.launch(Dispatchers.IO) { refreshSuspended() }
}
override val isRefreshing: MutableState<Boolean> = mutableStateOf(false)
@@ -100,14 +100,14 @@ open class UserFeedViewModel(
init {
Log.d("Init", "${this.javaClass.simpleName}")
viewModelScope.launch(Dispatchers.Default) {
viewModelScope.launch(Dispatchers.IO) {
LocalCache.live.newEventBundles.collect { newNotes ->
Log.d("Rendering Metrics", "Update feeds: ${this@UserFeedViewModel.javaClass.simpleName} with ${newNotes.size}")
invalidateData()
}
}
viewModelScope.launch(Dispatchers.Default) {
viewModelScope.launch(Dispatchers.IO) {
LocalCache.live.deletedEventBundles.collect { newNotes ->
Log.d("Rendering Metrics", "Delete from feeds: ${this@UserFeedViewModel.javaClass.simpleName} with ${newNotes.size}")
invalidateData()
@@ -106,6 +106,7 @@ import com.vitorpamplona.quartz.nip01Core.signers.SignerExceptions
import com.vitorpamplona.quartz.nip01Core.tags.people.PubKeyReferenceTag
import com.vitorpamplona.quartz.nip01Core.tags.people.isTaggedUser
import com.vitorpamplona.quartz.nip03Timestamp.EmptyOtsResolverBuilder
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKeyable
import com.vitorpamplona.quartz.nip18Reposts.GenericRepostEvent
import com.vitorpamplona.quartz.nip18Reposts.RepostEvent
@@ -202,7 +203,7 @@ class AccountViewModel(
val notificationHasNewItemsFlow =
notificationHasNewItems
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(30000), false)
@OptIn(ExperimentalCoroutinesApi::class)
@@ -251,7 +252,7 @@ class AccountViewModel(
val messagesHasNewItemsFlow =
messagesHasNewItems
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(30000), false)
@OptIn(ExperimentalCoroutinesApi::class)
@@ -284,7 +285,7 @@ class AccountViewModel(
val homeHasNewItemsFlow =
homeHasNewItems
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(30000), false)
val hasNewItems =
@@ -348,7 +349,10 @@ class AccountViewModel(
val isPostHidden = note.isHiddenFor(accountChoices)
val isHiddenAuthor = note.author?.let { account.isHidden(it) } == true
return if (isPostHidden) {
val noteEvent = note.event
val isDecryptedPostHidden = if (noteEvent is PrivateDmEvent) account.isDecryptedContentHidden(noteEvent) else false
return if (isPostHidden || isDecryptedPostHidden) {
// Spam + Blocked Users + Hidden Words + Sensitive Content
NoteComposeReportState(isPostHidden, false, false, isHiddenAuthor)
} else if (isFromLoggedIn || isFromLoggedInFollow) {
@@ -394,7 +398,7 @@ class AccountViewModel(
account.kind3FollowList.flow.value.authors,
),
)
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.IO)
.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(10000, 10000),
@@ -412,7 +416,7 @@ class AccountViewModel(
.relays
.stateFlow
.map { it.note.relays.size > 3 }
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.IO)
.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(10000, 10000),
@@ -431,7 +435,7 @@ class AccountViewModel(
suspend fun calculateZapAmount(zappedNote: Note): String =
if (zappedNote.zapPayments.isNotEmpty()) {
withContext(Dispatchers.Default) {
withContext(Dispatchers.IO) {
val it = account.calculateZappedAmount(zappedNote)
showAmount(it)
}
@@ -442,7 +446,7 @@ class AccountViewModel(
suspend fun calculateZapraiser(zappedNote: Note): ZapraiserStatus {
val zapraiserAmount = zappedNote.event?.zapraiserAmount() ?: 0
return if (zappedNote.zapPayments.isNotEmpty()) {
withContext(Dispatchers.Default) {
withContext(Dispatchers.IO) {
val newZapAmount = account.calculateZappedAmount(zappedNote)
var percentage = newZapAmount.div(zapraiserAmount.toBigDecimal()).toFloat()
@@ -1027,7 +1031,7 @@ class AccountViewModel(
fun cachedModificationEventsForNote(note: Note) = LocalCache.cachedModificationEventsForNote(note)
suspend fun findModificationEventsForNote(note: Note): List<Note> =
withContext(Dispatchers.Default) {
withContext(Dispatchers.IO) {
LocalCache.findLatestModificationForNote(note)
}
@@ -1067,7 +1071,7 @@ class AccountViewModel(
hexList: List<String>,
onReady: (ImmutableList<User>) -> Unit,
) {
viewModelScope.launch(Dispatchers.Default) {
viewModelScope.launch(Dispatchers.IO) {
onReady(loadUsersSync(hexList).toImmutableList())
}
}
@@ -1094,7 +1098,7 @@ class AccountViewModel(
val onIsNew = createdAt > lastTime
if (onIsNew) {
viewModelScope.launch(Dispatchers.Default) {
viewModelScope.launch(Dispatchers.IO) {
account.markAsRead(routeForLastRead, createdAt)
}
}
@@ -1142,7 +1146,7 @@ class AccountViewModel(
init {
Log.d("Init", "AccountViewModel")
viewModelScope.launch(Dispatchers.Default) {
viewModelScope.launch(Dispatchers.IO) {
feedStates.init()
// awaits for init to finish before starting to capture new events.
LocalCache.live.newEventBundles.collect { newNotes ->
@@ -1152,7 +1156,7 @@ class AccountViewModel(
}
}
viewModelScope.launch(Dispatchers.Default) {
viewModelScope.launch(Dispatchers.IO) {
LocalCache.live.deletedEventBundles.collect { newNotes ->
logTime("AccountViewModel deletedEventBundle Update with ${newNotes.size} new notes") {
feedStates.deleteNotes(newNotes)
@@ -1602,7 +1606,7 @@ class AccountViewModel(
val accountViewModel: AccountViewModel,
) : GenericBaseCache<String, LoadedBechLink>(20) {
override suspend fun compute(key: String): LoadedBechLink? =
withContext(Dispatchers.Default) {
withContext(Dispatchers.IO) {
Nip19Parser.uriToRoute(key)?.let {
var returningNote: Note? = null
@@ -1621,10 +1625,10 @@ class AccountViewModel(
}
}
is NEmbed ->
withContext(Dispatchers.Default) {
withContext(Dispatchers.IO) {
val baseNote = LocalCache.getOrCreateNote(parsed.event)
if (baseNote.event == null) {
launch(Dispatchers.Default) {
launch(Dispatchers.IO) {
LocalCache.justConsume(parsed.event, null, false)
}
}
@@ -61,7 +61,7 @@ abstract class ListChangeFeedViewModel(
init {
Log.d("Init", "Starting new Model: ${this.javaClass.simpleName}")
viewModelScope.launch(Dispatchers.Default) {
viewModelScope.launch(Dispatchers.IO) {
localFilter.changesFlow().collect {
Log.d("Init", "Collecting changes to: ${this@ListChangeFeedViewModel.javaClass.simpleName}")
when (it) {
@@ -361,7 +361,7 @@ class ChatNewMessageViewModel :
val version = draftTag.current
innerSendPost(null)
cancel()
accountViewModel.viewModelScope.launch(Dispatchers.Default) {
accountViewModel.viewModelScope.launch(Dispatchers.IO) {
accountViewModel.account.deleteDraftIgnoreErrors(version)
}
}
@@ -594,7 +594,7 @@ class ChatNewMessageViewModel :
}
fun updateRoomFromUsersInput() {
viewModelScope.launch(Dispatchers.Default) {
viewModelScope.launch(Dispatchers.IO) {
delay(300)
val toUsersTagger = NewMessageTagger(toUsers.text, null, null, accountViewModel)
toUsersTagger.run()
@@ -717,7 +717,7 @@ class ChatNewMessageViewModel :
}
override fun updateZapFromText() {
viewModelScope.launch(Dispatchers.Default) {
viewModelScope.launch(Dispatchers.IO) {
val tagger = NewMessageTagger(message.text, emptyList(), emptyList(), accountViewModel)
tagger.run()
tagger.pTags?.forEach { taggedUser ->
@@ -277,7 +277,7 @@ open class ChannelNewMessageViewModel :
cancel()
accountViewModel.account.signAndSendPrivately(template, channelRelays)
accountViewModel.viewModelScope.launch(Dispatchers.Default) {
accountViewModel.viewModelScope.launch(Dispatchers.IO) {
accountViewModel.account.deleteDraftIgnoreErrors(version)
}
}
@@ -319,7 +319,7 @@ open class ChannelNewMessageViewModel :
context: Context,
onceUploaded: suspend () -> Unit,
) {
viewModelScope.launch(Dispatchers.Default) {
viewModelScope.launch(Dispatchers.IO) {
val uploadState = uploadState ?: return@launch
val myMultiOrchestrator = uploadState.multiOrchestrator ?: return@launch
@@ -630,7 +630,7 @@ open class ChannelNewMessageViewModel :
}
fun updateZapFromText() {
viewModelScope.launch(Dispatchers.Default) {
viewModelScope.launch(Dispatchers.IO) {
val tagger = NewMessageTagger(message.text, emptyList(), emptyList(), accountViewModel)
tagger.run()
tagger.pTags?.forEach { taggedUser ->
@@ -61,12 +61,12 @@ class DMsFromUserFilterSubAssembler(
userJobMap[user]?.forEach { it.cancel() }
userJobMap[user] =
listOf(
key.account.scope.launch(Dispatchers.Default) {
key.account.scope.launch(Dispatchers.IO) {
key.account.homeRelays.flow.collectLatest {
invalidateFilters()
}
},
key.account.scope.launch(Dispatchers.Default) {
key.account.scope.launch(Dispatchers.IO) {
key.account.dmRelays.flow.collectLatest {
invalidateFilters()
}
@@ -54,7 +54,7 @@ class FollowingEphemeralChatSubAssembler(
userJobMap[key.account.userProfile()]?.forEach { it.cancel() }
userJobMap[key.account.userProfile()] =
listOf(
key.account.scope.launch(Dispatchers.Default) {
key.account.scope.launch(Dispatchers.IO) {
key.account.ephemeralChatList.liveEphemeralChatList.sample(500).collectLatest {
invalidateFilters()
}
@@ -55,7 +55,7 @@ class FollowingPublicChatSubAssembler(
userJobMap[key.account.userProfile()]?.forEach { it.cancel() }
userJobMap[key.account.userProfile()] =
listOf(
key.account.scope.launch(Dispatchers.Default) {
key.account.scope.launch(Dispatchers.IO) {
key.account.publicChatList.flowSet.sample(500).collectLatest {
invalidateFilters()
}

Some files were not shown because too many files have changed in this diff Show More