From 2b59ad04c7070bfa7730b0ad5d3638385198954c Mon Sep 17 00:00:00 2001 From: Taras Smakula <tarassmakula@gmail.com> Date: Tue, 2 Jan 2024 16:08:24 +0200 Subject: [PATCH] Change people tab categories --- .../feature/people/PeopleDataSource.kt | 131 +++++++++++++----- .../futo/circles/mapping/MatrixUserMapping.kt | 4 +- .../org/futo/circles/model/PeopleListItem.kt | 14 +- app/src/main/res/values/strings.xml | 1 + .../extensions/MatrixSessionExtensions.kt | 25 ++-- 5 files changed, 119 insertions(+), 56 deletions(-) diff --git a/app/src/main/java/org/futo/circles/feature/people/PeopleDataSource.kt b/app/src/main/java/org/futo/circles/feature/people/PeopleDataSource.kt index 8aec2dbca..a18cad47b 100644 --- a/app/src/main/java/org/futo/circles/feature/people/PeopleDataSource.kt +++ b/app/src/main/java/org/futo/circles/feature/people/PeopleDataSource.kt @@ -8,23 +8,32 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.withContext +import org.futo.circles.core.extensions.getRoomOwner import org.futo.circles.core.feature.room.knoks.KnockRequestsDataSource import org.futo.circles.core.feature.select_users.SearchUserDataSource import org.futo.circles.core.feature.workspace.SharedCircleDataSource +import org.futo.circles.core.feature.workspace.SpacesTreeAccountDataSource +import org.futo.circles.core.model.CIRCLES_SPACE_ACCOUNT_DATA_KEY import org.futo.circles.core.provider.MatrixSessionProvider +import org.futo.circles.core.utils.getJoinedRoomById +import org.futo.circles.core.utils.getTimelineRoomFor import org.futo.circles.mapping.toPeopleUserListItem import org.futo.circles.model.PeopleHeaderItem import org.futo.circles.model.PeopleItemType import org.futo.circles.model.PeopleListItem import org.futo.circles.model.PeopleRequestNotificationListItem -import org.matrix.android.sdk.api.session.getRoom +import org.matrix.android.sdk.api.session.room.model.Membership +import org.matrix.android.sdk.api.session.room.model.RoomSummary +import org.matrix.android.sdk.api.session.room.model.RoomType +import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.api.session.user.model.User import javax.inject.Inject class PeopleDataSource @Inject constructor( private val searchUserDataSource: SearchUserDataSource, private val sharedCircleDataSource: SharedCircleDataSource, - private val knockRequestsDataSource: KnockRequestsDataSource + private val knockRequestsDataSource: KnockRequestsDataSource, + private val spacesTreeAccountDataSource: SpacesTreeAccountDataSource ) { private val session = MatrixSessionProvider.currentSession @@ -35,18 +44,28 @@ class PeopleDataSource @Inject constructor( it.size }?.asFlow() ?: flowOf() + private fun getProfileSpaceInvitesCountFlow() = session?.roomService()?.getRoomSummariesLive( + roomSummaryQueryParams { + excludeType = null + memberships = listOf(Membership.INVITE) + })?.map { it.filter { it.roomType == RoomType.SPACE } }?.map { it.size } + ?.asFlow() ?: flowOf() + + suspend fun getPeopleList(query: String) = combine( searchUserDataSource.searchKnownUsers(query), searchUserDataSource.searchSuggestions(query), getIgnoredUserFlow(), - getKnockRequestCountFlow() - ) { knowUsers, suggestions, ignoredUsers, requestsCount -> + getKnockRequestCountFlow(), + getProfileSpaceInvitesCountFlow() + ) { knowUsers, suggestions, ignoredUsers, knocksCount, profileInvitesCount -> withContext(Dispatchers.IO) { buildList( knowUsers, suggestions, ignoredUsers, - requestsCount + knocksCount, + profileInvitesCount ) } }.distinctUntilChanged() @@ -63,54 +82,100 @@ class PeopleDataSource @Inject constructor( knowUsers: List<User>, suggestions: List<User>, ignoredUsers: List<User>, - requestsCount: Int + knocksCount: Int, + profileInvitesCount: Int ): List<PeopleListItem> { + val knownIds = knowUsers.map { it.userId } val ignoredUsersIds = ignoredUsers.map { it.userId }.toSet() - val uniqueItemsList = mutableListOf<PeopleListItem>().apply { - addAll(knowUsers.map { it.toPeopleUserListItem(getKnownUserItemType(it.userId)) }) - addAll(suggestions.map { it.toPeopleUserListItem(PeopleItemType.Suggestion) }) - } - .distinctBy { it.id } - .filterNot { it.id == session?.myUserId || ignoredUsersIds.contains(it.id) } + val followingUsersIds = getPeopleImFollowingIds() + val followersUsersIds = getFollowersIds() + val connectionsIds = + knowUsers.mapNotNull { if (isConnection(it.userId)) it.userId else null } + val otherMemberIds = + knownIds - connectionsIds.toSet() - followersUsersIds.toSet() - followingUsersIds.toSet() + val uniqueSuggestions = suggestions.filter { !knownIds.contains(it.userId) } + + val requestsCount = knocksCount + profileInvitesCount return mutableListOf<PeopleListItem>().apply { if (requestsCount > 0) add(PeopleRequestNotificationListItem(requestsCount)) addSection( - PeopleHeaderItem.friends, - uniqueItemsList.filter { it.type == PeopleItemType.Friend } + PeopleHeaderItem.connections, + knowUsers.mapNotNull { + if (connectionsIds.contains(it.userId)) it.toPeopleUserListItem( + PeopleItemType.Connection, + ignoredUsersIds.contains(it.userId) + ) else null + } ) addSection( PeopleHeaderItem.followingUsersHeader, - uniqueItemsList.filter { it.type == PeopleItemType.Following } + knowUsers.mapNotNull { + if (followingUsersIds.contains(it.userId)) it.toPeopleUserListItem( + PeopleItemType.Following, + ignoredUsersIds.contains(it.userId) + ) else null + } ) addSection( PeopleHeaderItem.followersUsersHeader, - uniqueItemsList.filter { it.type == PeopleItemType.Follower } + knowUsers.mapNotNull { + if (followersUsersIds.contains(it.userId)) it.toPeopleUserListItem( + PeopleItemType.Follower, + ignoredUsersIds.contains(it.userId) + ) else null + } ) addSection( - PeopleHeaderItem.knownUsersHeader, - uniqueItemsList.filter { it.type == PeopleItemType.Known } + PeopleHeaderItem.othersHeader, + knowUsers.mapNotNull { + if (otherMemberIds.contains(it.userId)) it.toPeopleUserListItem( + PeopleItemType.Others, + ignoredUsersIds.contains(it.userId) + ) else null + } ) addSection( PeopleHeaderItem.suggestions, - uniqueItemsList.filter { it.type == PeopleItemType.Suggestion } + uniqueSuggestions.map { + it.toPeopleUserListItem( + PeopleItemType.Suggestion, + ignoredUsersIds.contains(it.userId) + ) + } ) } } - private fun getKnownUserItemType(userId: String): PeopleItemType { - val isFollower = isMyFollower(userId) - val amIFollowing = amIFollowing(userId) - val isFriend = isFollower && amIFollowing + //All the joined members (except me) in all of my circle timeline rooms + private fun getFollowersIds(): List<String> { + val myCirclesSpace = getMyCirclesSpaceSummary() ?: return emptyList() + val myTimelinesFollowers = myCirclesSpace.spaceChildren?.mapNotNull { + getTimelineRoomFor(it.childRoomId)?.roomSummary()?.otherMemberIds + }?.flatMap { it.toSet() } ?: emptyList() - return when { - isFriend -> PeopleItemType.Friend - amIFollowing -> PeopleItemType.Following - isFollower -> PeopleItemType.Follower - else -> PeopleItemType.Known - } + return myTimelinesFollowers + } + + //All the creators of all the timeline rooms that I'm following in my circles + private fun getPeopleImFollowingIds(): List<String> { + val myCirclesSpace = getMyCirclesSpaceSummary() ?: return emptyList() + val peopleIamFollowing = myCirclesSpace.spaceChildren?.mapNotNull { + getJoinedRoomById(it.childRoomId)?.roomSummary()?.spaceChildren?.mapNotNull { + getRoomOwner(it.childRoomId)?.userId?.takeIf { it != session?.myUserId } + } + }?.flatMap { it.toSet() } ?: emptyList() + + return peopleIamFollowing + } + + private fun getMyCirclesSpaceSummary(): RoomSummary? { + val circlesSpaceId = spacesTreeAccountDataSource.getRoomIdByKey( + CIRCLES_SPACE_ACCOUNT_DATA_KEY + ) ?: "" + return getJoinedRoomById(circlesSpaceId)?.roomSummary() } private fun MutableList<PeopleListItem>.addSection( @@ -123,13 +188,7 @@ class PeopleDataSource @Inject constructor( } } - private fun isMyFollower(userId: String): Boolean { - val mySharedCircleMembers = - session?.getRoom(profileRoomId)?.roomSummary()?.otherMemberIds ?: emptyList() - return mySharedCircleMembers.contains(userId) - } - - private fun amIFollowing(userId: String) = + private fun isConnection(userId: String) = sharedCircleDataSource.getSharedCircleFor(userId) != null } \ No newline at end of file diff --git a/app/src/main/java/org/futo/circles/mapping/MatrixUserMapping.kt b/app/src/main/java/org/futo/circles/mapping/MatrixUserMapping.kt index 2dd4b77ac..5466958d5 100644 --- a/app/src/main/java/org/futo/circles/mapping/MatrixUserMapping.kt +++ b/app/src/main/java/org/futo/circles/mapping/MatrixUserMapping.kt @@ -6,7 +6,7 @@ import org.futo.circles.model.PeopleUserListItem import org.matrix.android.sdk.api.session.user.model.User -fun User.toPeopleUserListItem(type: PeopleItemType) = - PeopleUserListItem(toCirclesUserSummary(), type) +fun User.toPeopleUserListItem(type: PeopleItemType, isIgnored: Boolean) = + PeopleUserListItem(toCirclesUserSummary(), type, isIgnored) diff --git a/app/src/main/java/org/futo/circles/model/PeopleListItem.kt b/app/src/main/java/org/futo/circles/model/PeopleListItem.kt index 1fa1cf1f7..66127074e 100644 --- a/app/src/main/java/org/futo/circles/model/PeopleListItem.kt +++ b/app/src/main/java/org/futo/circles/model/PeopleListItem.kt @@ -4,7 +4,7 @@ import org.futo.circles.R import org.futo.circles.core.base.list.IdEntity import org.futo.circles.core.model.CirclesUserSummary -enum class PeopleItemType { Header, Friend, Following, Follower, RequestNotification, Known, Suggestion } +enum class PeopleItemType { Header, Connection, Following, Follower, RequestNotification, Others, Suggestion } sealed class PeopleListItem( open val type: PeopleItemType ) : IdEntity<String> @@ -15,17 +15,19 @@ data class PeopleHeaderItem( override val id: String = titleRes.toString() companion object { - val friends = PeopleHeaderItem(org.futo.circles.auth.R.string.friends) - val followersUsersHeader = PeopleHeaderItem(R.string.followers) - val followingUsersHeader = PeopleHeaderItem(org.futo.circles.core.R.string.following) - val knownUsersHeader = PeopleHeaderItem(R.string.known_users) + val connections = PeopleHeaderItem(R.string.my_connections) + val followersUsersHeader = PeopleHeaderItem(org.futo.circles.core.R.string.my_followers) + val followingUsersHeader = + PeopleHeaderItem(org.futo.circles.core.R.string.people_i_m_following) + val othersHeader = PeopleHeaderItem(org.futo.circles.core.R.string.others) val suggestions = PeopleHeaderItem(R.string.suggestions) } } class PeopleUserListItem( val user: CirclesUserSummary, - override val type: PeopleItemType + override val type: PeopleItemType, + val isIgnored: Boolean ) : PeopleListItem(type) { override val id: String = user.id } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0a7734f17..bcc468ab7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -203,6 +203,7 @@ <string name="group_invites_notification_format">Show %d Group invitations</string> <string name="circle_invites_notification_format">Show %d Circle invitations</string> <string name="show_follow_requests_format">Show %d follow requests</string> + <string name="my_connections">My Connections</string> <!-- Rich text editor --> <string name="rich_text_editor_format_bold">Apply bold format</string> diff --git a/core/src/main/java/org/futo/circles/core/extensions/MatrixSessionExtensions.kt b/core/src/main/java/org/futo/circles/core/extensions/MatrixSessionExtensions.kt index ea448a20b..67e555547 100644 --- a/core/src/main/java/org/futo/circles/core/extensions/MatrixSessionExtensions.kt +++ b/core/src/main/java/org/futo/circles/core/extensions/MatrixSessionExtensions.kt @@ -54,16 +54,17 @@ suspend fun Session.getOrFetchUser(userId: String): User = getUser(userId) ?: userService().resolveUser(userId) fun Session.getKnownUsersFlow() = roomService().getRoomSummariesLive(roomSummaryQueryParams { - memberships = listOf(Membership.JOIN) - }).asFlow() - .mapLatest { roomSummaries -> - val knowUsers = mutableSetOf<User>() - roomSummaries.forEach { summary -> - val joinedMembersIds = getRoom(summary.roomId)?.membershipService() - ?.getRoomMembers(roomMemberQueryParams { - memberships = listOf(Membership.JOIN) - })?.map { it.userId } ?: emptyList() - joinedMembersIds.forEach { knowUsers.add(getOrFetchUser(it)) } - } - knowUsers.toList().filterNot { getUserIdsToExclude().contains(it.userId) } + excludeType = null + memberships = listOf(Membership.JOIN) +}).asFlow() + .mapLatest { roomSummaries -> + val knowUsers = mutableSetOf<User>() + roomSummaries.forEach { summary -> + val joinedMembersIds = getRoom(summary.roomId)?.membershipService() + ?.getRoomMembers(roomMemberQueryParams { + memberships = listOf(Membership.JOIN) + })?.map { it.userId } ?: emptyList() + joinedMembersIds.forEach { knowUsers.add(getOrFetchUser(it)) } } + knowUsers.toList().filterNot { getUserIdsToExclude().contains(it.userId) } + } -- GitLab