mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-11-10 23:36:42 +01:00
Replace first hack with a better solution for determining a follow.
Add FollowSetState to MergedFollowListsState and modify it to take into account users from follow sets when displaying a user's follows feed.
This commit is contained in:
@@ -20,22 +20,25 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.amethyst.model.nip51Lists.followSets
|
package com.vitorpamplona.amethyst.model.nip51Lists.followSets
|
||||||
|
|
||||||
import androidx.compose.ui.util.fastAny
|
|
||||||
import com.vitorpamplona.amethyst.model.LocalCache
|
import com.vitorpamplona.amethyst.model.LocalCache
|
||||||
import com.vitorpamplona.amethyst.model.Note
|
import com.vitorpamplona.amethyst.model.Note
|
||||||
import com.vitorpamplona.amethyst.model.User
|
import com.vitorpamplona.amethyst.model.User
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.value
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||||
import com.vitorpamplona.quartz.nip51Lists.peopleList.PeopleListEvent
|
import com.vitorpamplona.quartz.nip51Lists.peopleList.PeopleListEvent
|
||||||
import com.vitorpamplona.quartz.utils.Log
|
import com.vitorpamplona.quartz.utils.Log
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.catch
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flowOn
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.onCompletion
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
class FollowSetState(
|
class FollowSetState(
|
||||||
@@ -44,20 +47,24 @@ class FollowSetState(
|
|||||||
val scope: CoroutineScope,
|
val scope: CoroutineScope,
|
||||||
) {
|
) {
|
||||||
val user = cache.getOrCreateUser(signer.pubKey)
|
val user = cache.getOrCreateUser(signer.pubKey)
|
||||||
|
private val isActive = MutableStateFlow(false)
|
||||||
|
|
||||||
suspend fun getFollowSetNotes() =
|
suspend fun getFollowSetNotes() =
|
||||||
withContext(Dispatchers.Default) {
|
withContext(Dispatchers.Default) {
|
||||||
val followSetNotes = LocalCache.getFollowSetNotesFor(user)
|
val followSetNotes = LocalCache.getFollowSetNotesFor(user)
|
||||||
Log.d(this.javaClass.simpleName, "Number of follow sets: ${followSetNotes.size}")
|
Log.d(this@FollowSetState.javaClass.simpleName, "Number of follow sets: ${followSetNotes.size}")
|
||||||
return@withContext followSetNotes
|
return@withContext followSetNotes
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getFollowSetNotesFlow() =
|
private fun getFollowSetNotesFlow() =
|
||||||
flow {
|
flow {
|
||||||
val followSetNotes = getFollowSetNotes()
|
while (isActive.value) {
|
||||||
val followSets = followSetNotes.map { mapNoteToFollowSet(it) }
|
val followSetNotes = getFollowSetNotes()
|
||||||
emit(followSets)
|
val followSets = followSetNotes.map { mapNoteToFollowSet(it) }
|
||||||
}.flowOn(Dispatchers.IO)
|
emit(followSets)
|
||||||
|
delay(1000)
|
||||||
|
}
|
||||||
|
}.flowOn(Dispatchers.Default)
|
||||||
|
|
||||||
val profilesFlow =
|
val profilesFlow =
|
||||||
getFollowSetNotesFlow()
|
getFollowSetNotesFlow()
|
||||||
@@ -65,27 +72,25 @@ class FollowSetState(
|
|||||||
it.flatMapTo(mutableSetOf()) { it.profiles }.toSet()
|
it.flatMapTo(mutableSetOf()) { it.profiles }.toSet()
|
||||||
}.stateIn(scope, SharingStarted.Eagerly, emptySet())
|
}.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 =
|
fun mapNoteToFollowSet(note: Note): FollowSet =
|
||||||
FollowSet
|
FollowSet
|
||||||
.mapEventToSet(
|
.mapEventToSet(
|
||||||
event = note.event as PeopleListEvent,
|
event = note.event as PeopleListEvent,
|
||||||
signer,
|
signer,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun isUserInFollowSets(user: User): Boolean = profilesFlow.value.contains(user.pubkeyHex)
|
||||||
|
|
||||||
|
init {
|
||||||
|
isActive.update { true }
|
||||||
|
scope.launch(Dispatchers.Default) {
|
||||||
|
getFollowSetNotesFlow()
|
||||||
|
.onCompletion {
|
||||||
|
isActive.update { false }
|
||||||
|
}.catch {
|
||||||
|
Log.e(this@FollowSetState.javaClass.simpleName, "Error on flow collection: ${it.message}")
|
||||||
|
isActive.update { false }
|
||||||
|
}.collect {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
package com.vitorpamplona.amethyst.model.serverList
|
package com.vitorpamplona.amethyst.model.serverList
|
||||||
|
|
||||||
import com.vitorpamplona.amethyst.model.nip02FollowLists.FollowListState
|
import com.vitorpamplona.amethyst.model.nip02FollowLists.FollowListState
|
||||||
|
import com.vitorpamplona.amethyst.model.nip51Lists.followSets.FollowSetState
|
||||||
import com.vitorpamplona.amethyst.model.nip51Lists.geohashLists.GeohashListState
|
import com.vitorpamplona.amethyst.model.nip51Lists.geohashLists.GeohashListState
|
||||||
import com.vitorpamplona.amethyst.model.nip51Lists.hashtagLists.HashtagListState
|
import com.vitorpamplona.amethyst.model.nip51Lists.hashtagLists.HashtagListState
|
||||||
import com.vitorpamplona.amethyst.model.nip72Communities.CommunityListState
|
import com.vitorpamplona.amethyst.model.nip72Communities.CommunityListState
|
||||||
@@ -37,6 +38,7 @@ import kotlinx.coroutines.flow.stateIn
|
|||||||
|
|
||||||
class MergedFollowListsState(
|
class MergedFollowListsState(
|
||||||
val kind3List: FollowListState,
|
val kind3List: FollowListState,
|
||||||
|
val followSetList: FollowSetState,
|
||||||
val hashtagList: HashtagListState,
|
val hashtagList: HashtagListState,
|
||||||
val geohashList: GeohashListState,
|
val geohashList: GeohashListState,
|
||||||
val communityList: CommunityListState,
|
val communityList: CommunityListState,
|
||||||
@@ -44,12 +46,13 @@ class MergedFollowListsState(
|
|||||||
) {
|
) {
|
||||||
fun mergeLists(
|
fun mergeLists(
|
||||||
kind3: FollowListState.Kind3Follows,
|
kind3: FollowListState.Kind3Follows,
|
||||||
|
followSetProfiles: Set<String>,
|
||||||
hashtags: Set<String>,
|
hashtags: Set<String>,
|
||||||
geohashes: Set<String>,
|
geohashes: Set<String>,
|
||||||
community: Set<CommunityTag>,
|
community: Set<CommunityTag>,
|
||||||
): FollowListState.Kind3Follows =
|
): FollowListState.Kind3Follows =
|
||||||
FollowListState.Kind3Follows(
|
FollowListState.Kind3Follows(
|
||||||
kind3.authors,
|
kind3.authors + followSetProfiles,
|
||||||
kind3.authorsPlusMe,
|
kind3.authorsPlusMe,
|
||||||
kind3.hashtags + hashtags,
|
kind3.hashtags + hashtags,
|
||||||
kind3.geotags + geohashes,
|
kind3.geotags + geohashes,
|
||||||
@@ -59,15 +62,17 @@ class MergedFollowListsState(
|
|||||||
val flow: StateFlow<FollowListState.Kind3Follows> =
|
val flow: StateFlow<FollowListState.Kind3Follows> =
|
||||||
combine(
|
combine(
|
||||||
kind3List.flow,
|
kind3List.flow,
|
||||||
|
followSetList.profilesFlow,
|
||||||
hashtagList.flow,
|
hashtagList.flow,
|
||||||
geohashList.flow,
|
geohashList.flow,
|
||||||
communityList.flow,
|
communityList.flow,
|
||||||
) { kind3, hashtag, geohash, community ->
|
) { kind3, followSet, hashtag, geohash, community ->
|
||||||
mergeLists(kind3, hashtag, geohash, community)
|
mergeLists(kind3, followSet, hashtag, geohash, community)
|
||||||
}.onStart {
|
}.onStart {
|
||||||
emit(
|
emit(
|
||||||
mergeLists(
|
mergeLists(
|
||||||
kind3List.flow.value,
|
kind3List.flow.value,
|
||||||
|
followSetList.profilesFlow.value,
|
||||||
hashtagList.flow.value,
|
hashtagList.flow.value,
|
||||||
geohashList.flow.value,
|
geohashList.flow.value,
|
||||||
communityList.flow.value,
|
communityList.flow.value,
|
||||||
|
|||||||
@@ -411,6 +411,8 @@ fun observeUserIsFollowing(
|
|||||||
): State<Boolean> {
|
): State<Boolean> {
|
||||||
// Subscribe in the relay for changes in the metadata of this user.
|
// Subscribe in the relay for changes in the metadata of this user.
|
||||||
UserFinderFilterAssemblerSubscription(user1, accountViewModel)
|
UserFinderFilterAssemblerSubscription(user1, accountViewModel)
|
||||||
|
val isUserInFollowSets = accountViewModel.account.followSetsState.isUserInFollowSets(user2)
|
||||||
|
println("Is ${user2.toBestDisplayName()} in a Follow set? $isUserInFollowSets")
|
||||||
|
|
||||||
// Subscribe in the LocalCache for changes that arrive in the device
|
// Subscribe in the LocalCache for changes that arrive in the device
|
||||||
val flow =
|
val flow =
|
||||||
@@ -420,14 +422,13 @@ fun observeUserIsFollowing(
|
|||||||
.follows.stateFlow
|
.follows.stateFlow
|
||||||
.sample(1000)
|
.sample(1000)
|
||||||
.mapLatest { userState ->
|
.mapLatest { userState ->
|
||||||
userState.user.isFollowing(user2) ||
|
userState.user.isFollowing(user2) || isUserInFollowSets
|
||||||
accountViewModel.account.isUserInFollowSets(user2)
|
|
||||||
}.distinctUntilChanged()
|
}.distinctUntilChanged()
|
||||||
.flowOn(Dispatchers.Default)
|
.flowOn(Dispatchers.Default)
|
||||||
}
|
}
|
||||||
|
|
||||||
return flow.collectAsStateWithLifecycle(
|
return flow.collectAsStateWithLifecycle(
|
||||||
user1.isFollowing(user2) || accountViewModel.account.isUserInFollowSets(user2),
|
user1.isFollowing(user2) || isUserInFollowSets,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user