Refactor: Create FollowSetState and move functionality from other places to it. Modify usages accordingly.

This commit is contained in:
KotlinGeekDev
2025-09-25 21:39:02 +01:00
parent b694e143d0
commit 6368d17d09
3 changed files with 101 additions and 43 deletions
@@ -21,7 +21,6 @@
package com.vitorpamplona.amethyst.model
import androidx.compose.runtime.Stable
import androidx.compose.ui.util.fastAny
import com.vitorpamplona.amethyst.BuildConfig
import com.vitorpamplona.amethyst.LocalPreferences
import com.vitorpamplona.amethyst.commons.richtext.RichTextParser
@@ -58,6 +57,7 @@ import com.vitorpamplona.amethyst.model.nip51Lists.blockedRelays.BlockedRelayLis
import com.vitorpamplona.amethyst.model.nip51Lists.blockedRelays.BlockedRelayListState
import com.vitorpamplona.amethyst.model.nip51Lists.broadcastRelays.BroadcastRelayListDecryptionCache
import com.vitorpamplona.amethyst.model.nip51Lists.broadcastRelays.BroadcastRelayListState
import com.vitorpamplona.amethyst.model.nip51Lists.followSets.FollowSetState
import com.vitorpamplona.amethyst.model.nip51Lists.geohashLists.GeohashListDecryptionCache
import com.vitorpamplona.amethyst.model.nip51Lists.geohashLists.GeohashListState
import com.vitorpamplona.amethyst.model.nip51Lists.hashtagLists.HashtagListDecryptionCache
@@ -94,7 +94,6 @@ import com.vitorpamplona.amethyst.service.location.LocationState
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.nwc.NWCPaymentFilterAssembler
import com.vitorpamplona.amethyst.service.uploads.FileHeader
import com.vitorpamplona.amethyst.ui.screen.loggedIn.EventProcessor
import com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.FollowSet
import com.vitorpamplona.quartz.experimental.bounties.BountyAddValueEvent
import com.vitorpamplona.quartz.experimental.edits.TextNoteModificationEvent
import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStoryBaseEvent
@@ -217,8 +216,6 @@ import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import java.math.BigDecimal
import java.util.Locale
import kotlin.coroutines.cancellation.CancellationException
@@ -270,6 +267,7 @@ class Account(
val blockedRelayList = BlockedRelayListState(signer, cache, blockedRelayListDecryptionCache, scope, settings)
val kind3FollowList = FollowListState(signer, cache, scope, settings)
val followSetsState = FollowSetState(signer, cache, scope)
val ephemeralChatListDecryptionCache = EphemeralChatListDecryptionCache(signer)
val ephemeralChatList = EphemeralChatListState(signer, cache, ephemeralChatListDecryptionCache, scope, settings)
@@ -322,7 +320,7 @@ class Account(
val followsPerRelay = FollowsPerOutboxRelay(kind3FollowList, blockedRelayList, proxyRelayList, cache, scope).flow
// Merges all follow lists to create a single All Follows feed.
val allFollows = MergedFollowListsState(kind3FollowList, hashtagList, geohashList, communityList, scope)
val allFollows = MergedFollowListsState(kind3FollowList, followSetsState, hashtagList, geohashList, communityList, scope)
val privateDMDecryptionCache = PrivateDMCache(signer)
val privateZapsDecryptionCache = PrivateZapCache(signer)
@@ -834,37 +832,6 @@ class Account(
fun upgradeAttestations() = otsState.upgradeAttestationsIfNeeded(::sendAutomatic)
suspend fun getFollowSetNotes() =
withContext(Dispatchers.Default) {
val followSetNotes = LocalCache.getFollowSetNotesFor(userProfile())
Log.d(this@Account.javaClass.simpleName, "Number of follow sets: ${followSetNotes.size}")
return@withContext followSetNotes
}
fun isUserInFollowSets(user: User): Boolean =
runBlocking(scope.coroutineContext) {
LocalCache.getFollowSetNotesFor(userProfile()).fastAny { it ->
val listEvent = it.event as PeopleListEvent
val isInPublicSets =
listEvent
.publicPeople()
.fastAny { it.toTagArray().value() == user.pubkeyHex }
val isInPrivateSets =
listEvent
.privatePeople(signer)
?.fastAny { it.toTagArray().value() == user.pubkeyHex } ?: false
isInPublicSets || isInPrivateSets
}
}
fun mapNoteToFollowSet(note: Note): FollowSet =
FollowSet
.mapEventToSet(
event = note.event as PeopleListEvent,
signer,
)
suspend fun follow(user: User) = sendMyPublicAndPrivateOutbox(kind3FollowList.follow(user))
suspend fun unfollow(user: User) = sendMyPublicAndPrivateOutbox(kind3FollowList.unfollow(user))
@@ -0,0 +1,91 @@
/**
* Copyright (c) 2025 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.amethyst.model.nip51Lists.followSets
import androidx.compose.ui.util.fastAny
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.quartz.nip01Core.core.value
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip51Lists.peopleList.PeopleListEvent
import com.vitorpamplona.quartz.utils.Log
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
class FollowSetState(
val signer: NostrSigner,
val cache: LocalCache,
val scope: CoroutineScope,
) {
val user = cache.getOrCreateUser(signer.pubKey)
suspend fun getFollowSetNotes() =
withContext(Dispatchers.Default) {
val followSetNotes = LocalCache.getFollowSetNotesFor(user)
Log.d(this.javaClass.simpleName, "Number of follow sets: ${followSetNotes.size}")
return@withContext followSetNotes
}
private fun getFollowSetNotesFlow() =
flow {
val followSetNotes = getFollowSetNotes()
val followSets = followSetNotes.map { mapNoteToFollowSet(it) }
emit(followSets)
}.flowOn(Dispatchers.IO)
val profilesFlow =
getFollowSetNotesFlow()
.map { it ->
it.flatMapTo(mutableSetOf()) { it.profiles }.toSet()
}.stateIn(scope, SharingStarted.Eagerly, emptySet())
fun isUserInFollowSets(user: User): Boolean =
runBlocking(scope.coroutineContext) {
LocalCache.getFollowSetNotesFor(user).fastAny { it ->
val listEvent = it.event as PeopleListEvent
val isInPublicSets =
listEvent
.publicPeople()
.fastAny { it.toTagArray().value() == user.pubkeyHex }
val isInPrivateSets =
listEvent
.privatePeople(signer)
?.fastAny { it.toTagArray().value() == user.pubkeyHex } ?: false
isInPublicSets || isInPrivateSets
}
}
fun mapNoteToFollowSet(note: Note): FollowSet =
FollowSet
.mapEventToSet(
event = note.event as PeopleListEvent,
signer,
)
}
@@ -21,20 +21,20 @@
package com.vitorpamplona.amethyst.ui.dal
import android.util.Log
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.FollowSet
import com.vitorpamplona.amethyst.model.nip51Lists.followSets.FollowSet
import com.vitorpamplona.amethyst.model.nip51Lists.followSets.FollowSetState
import kotlinx.coroutines.runBlocking
class FollowSetFeedFilter(
val account: Account,
val followSetState: FollowSetState,
) : FeedFilter<FollowSet>() {
override fun feedKey(): String = account.userProfile().pubkeyHex + "-followsets"
override fun feedKey(): String = followSetState.user.pubkeyHex + "-followsets"
override fun feed(): List<FollowSet> =
runBlocking(account.scope.coroutineContext) {
runBlocking(followSetState.scope.coroutineContext) {
try {
val fetchedSets = account.getFollowSetNotes()
val followSets = fetchedSets.map { account.mapNoteToFollowSet(it) }
val fetchedSets = followSetState.getFollowSetNotes()
val followSets = fetchedSets.map { followSetState.mapNoteToFollowSet(it) }
println("Updated follow set size for feed filter: ${followSets.size}")
followSets
} catch (e: Exception) {