From e735aee7246cd4fdb7ad83887ded3deee0855667 Mon Sep 17 00:00:00 2001 From: Valere Date: Wed, 14 Apr 2021 14:43:21 +0200 Subject: [PATCH] Fix / group switch + refactoring --- .../session/room/RoomSummaryQueryParams.kt | 7 +- .../database/RealmSessionStoreMigration.kt | 1 + .../database/model/RoomSummaryEntity.kt | 5 + .../room/summary/RoomSummaryDataSource.kt | 5 + .../room/summary/RoomSummaryUpdater.kt | 33 ++ .../java/im/vector/app/AppStateHandler.kt | 2 +- .../app/features/home/HomeDetailFragment.kt | 45 ++- .../app/features/home/HomeDetailViewModel.kt | 7 + .../app/features/home/HomeDetailViewState.kt | 2 + .../room/list/GroupRoomListSectionBuilder.kt | 261 ++++++++++++ .../home/room/list/RoomListSectionBuilder.kt | 23 ++ .../home/room/list/RoomListViewModel.kt | 358 ++-------------- .../room/list/RoomListViewModelFactory.kt | 10 +- .../room/list/SpaceRoomListSectionBuilder.kt | 381 ++++++++++++++++++ 14 files changed, 796 insertions(+), 344 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/home/room/list/GroupRoomListSectionBuilder.kt create mode 100644 vector/src/main/java/im/vector/app/features/home/room/list/RoomListSectionBuilder.kt create mode 100644 vector/src/main/java/im/vector/app/features/home/room/list/SpaceRoomListSectionBuilder.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomSummaryQueryParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomSummaryQueryParams.kt index 0b1db09773..42dbecdaad 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomSummaryQueryParams.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomSummaryQueryParams.kt @@ -58,7 +58,8 @@ data class RoomSummaryQueryParams( val roomTagQueryFilter: RoomTagQueryFilter?, val excludeType: List?, val includeType: List?, - val activeSpaceId: ActiveSpaceFilter? + val activeSpaceId: ActiveSpaceFilter?, + var activeGroupId: String? = null ) { class Builder { @@ -72,6 +73,7 @@ data class RoomSummaryQueryParams( var excludeType: List? = listOf(RoomType.SPACE) var includeType: List? = null var activeSpaceId: ActiveSpaceFilter = ActiveSpaceFilter.None + var activeGroupId: String? = null fun build() = RoomSummaryQueryParams( roomId = roomId, @@ -82,7 +84,8 @@ data class RoomSummaryQueryParams( roomTagQueryFilter = roomTagQueryFilter, excludeType = excludeType, includeType = includeType, - activeSpaceId = activeSpaceId + activeSpaceId = activeSpaceId, + activeGroupId = activeGroupId ) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt index 5e126a1536..477955cbaf 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt @@ -218,6 +218,7 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration { realm.schema.get("RoomSummaryEntity") ?.addField(RoomSummaryEntityFields.ROOM_TYPE, String::class.java) ?.addField(RoomSummaryEntityFields.FLATTEN_PARENT_IDS, String::class.java) + ?.addField(RoomSummaryEntityFields.GROUP_IDS, String::class.java) ?.transform { obj -> // Should I put messaging type here? obj.setString(RoomSummaryEntityFields.ROOM_TYPE, null) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomSummaryEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomSummaryEntity.kt index 0adcec067e..4f47032c4d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomSummaryEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomSummaryEntity.kt @@ -212,6 +212,11 @@ internal open class RoomSummaryEntity( if (value != field) field = value } + var groupIds: String? = null + set(value) { + if (value != field) field = value + } + @Index private var membershipStr: String = Membership.NONE.name diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryDataSource.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryDataSource.kt index 24f52380f4..633706de1e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryDataSource.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryDataSource.kt @@ -197,6 +197,7 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat override fun updateQuery(builder: (RoomSummaryQueryParams) -> RoomSummaryQueryParams) { realmDataSourceFactory.updateQuery { roomSummariesQuery(it, builder.invoke(queryParams)) + .sort(RoomSummaryEntityFields.LAST_ACTIVITY_TIME, Sort.DESCENDING) } } } @@ -275,6 +276,10 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat // nop } } + + if (queryParams.activeGroupId != null) { + query.contains(RoomSummaryEntityFields.GROUP_IDS, queryParams.activeGroupId) + } return query } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt index 25dd6a214e..0dce2d2c67 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt @@ -37,6 +37,7 @@ import org.matrix.android.sdk.internal.database.mapper.asDomain import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity import org.matrix.android.sdk.internal.database.model.EventEntity import org.matrix.android.sdk.internal.database.model.EventEntityFields +import org.matrix.android.sdk.internal.database.model.GroupSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFields import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields @@ -322,6 +323,38 @@ internal class RoomSummaryUpdater @Inject constructor( // we need also to filter DMs... // it's more annoying as based on if the other members belong the space or not + + // LEGACY GROUPS + // lets mark rooms that belongs to groups + val existingGroups = GroupSummaryEntity.where(realm).findAll() + + // For rooms + realm.where(RoomSummaryEntity::class.java) + .process(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.activeMemberships()) + .equalTo(RoomSummaryEntityFields.IS_DIRECT, false) + .findAll().forEach { room -> + val belongsTo = existingGroups.filter { it.roomIds.contains(room.roomId) } + room.groupIds = if (belongsTo.isEmpty()) { + null + } else { + "|${belongsTo.joinToString("|")}|" + } + } + + // For DMS + realm.where(RoomSummaryEntity::class.java) + .process(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.activeMemberships()) + .equalTo(RoomSummaryEntityFields.IS_DIRECT, true) + .findAll().forEach { room -> + val belongsTo = existingGroups.filter { + it.userIds.intersect(room.otherMemberIds).isNotEmpty() + } + room.groupIds = if (belongsTo.isEmpty()) { + null + } else { + "|${belongsTo.joinToString("|")}|" + } + } }.also { Timber.v("## SPACES: Finish checking room hierarchy in $it ms") } diff --git a/vector/src/main/java/im/vector/app/AppStateHandler.kt b/vector/src/main/java/im/vector/app/AppStateHandler.kt index bb45e97776..747ca62193 100644 --- a/vector/src/main/java/im/vector/app/AppStateHandler.kt +++ b/vector/src/main/java/im/vector/app/AppStateHandler.kt @@ -35,7 +35,7 @@ import javax.inject.Singleton // TODO Keep this class for now, will maybe be used fro Space @Singleton class AppStateHandler @Inject constructor( - private val sessionDataSource: ActiveSessionDataSource, + sessionDataSource: ActiveSessionDataSource, private val uiStateRepository: UiStateRepository ) : LifecycleObserver { diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt index 572a13a084..607a1aeb54 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt @@ -131,11 +131,15 @@ class HomeDetailFragment @Inject constructor( } viewModel.selectSubscribe(this, HomeDetailViewState::groupSummary) { groupSummary -> - onGroupChange(groupSummary.orNull()) + if (!vectorPreferences.labSpaces()) { + onGroupChange(groupSummary.orNull()) + } } viewModel.selectSubscribe(this, HomeDetailViewState::spaceSummary) { spaceSummary -> - onSpaceChange(spaceSummary.orNull()) + if (vectorPreferences.labSpaces()) { + onSpaceChange(spaceSummary.orNull()) + } } viewModel.selectSubscribe(this, HomeDetailViewState::displayMode) { displayMode -> @@ -245,9 +249,23 @@ class HomeDetailFragment @Inject constructor( } private fun onGroupChange(groupSummary: GroupSummary?) { - groupSummary?.let { + groupSummary ?: return + if (groupSummary.groupId == ALL_COMMUNITIES_GROUP_ID) { + // Special case + avatarRenderer.clear(views.groupToolbarAvatarImageView) + views.groupToolbarAvatarImageView.background = null + + val myMxItem = withState(viewModel) { it.myMatrixItem } + if (myMxItem != null) { + avatarRenderer.render(myMxItem, views.groupToolbarAvatarImageView, GlideApp.with(requireActivity())) + } + views.groupToolbarSpaceTitleView.isVisible = false + } else { + views.groupToolbarAvatarImageView.background = null // Use GlideApp with activity context to avoid the glideRequests to be paused - avatarRenderer.render(it.toMatrixItem(), views.groupToolbarAvatarImageView, GlideApp.with(requireActivity())) + avatarRenderer.render(groupSummary.toMatrixItem(), views.groupToolbarAvatarImageView, GlideApp.with(requireActivity())) + views.groupToolbarSpaceTitleView.isVisible = true + views.groupToolbarSpaceTitleView.text = groupSummary.displayName } } @@ -268,6 +286,7 @@ class HomeDetailFragment @Inject constructor( views.groupToolbarSpaceTitleView.isVisible = false } else { + avatarRenderer.clear(views.groupToolbarAvatarImageView) views.groupToolbarAvatarImageView.background = null avatarRenderer.renderSpace(spaceSummary.toMatrixItem(), views.groupToolbarAvatarImageView, GlideApp.with(requireActivity())) views.groupToolbarSpaceTitleView.isVisible = true @@ -279,10 +298,10 @@ class HomeDetailFragment @Inject constructor( serverBackupStatusViewModel .subscribe(this) { when (val banState = it.bannerState.invoke()) { - is BannerState.Setup -> views.homeKeysBackupBanner.render(KeysBackupBanner.State.Setup(banState.numberOfKeys), false) + is BannerState.Setup -> views.homeKeysBackupBanner.render(KeysBackupBanner.State.Setup(banState.numberOfKeys), false) BannerState.BackingUp -> views.homeKeysBackupBanner.render(KeysBackupBanner.State.BackingUp, false) null, - BannerState.Hidden -> views.homeKeysBackupBanner.render(KeysBackupBanner.State.Hidden, false) + BannerState.Hidden -> views.homeKeysBackupBanner.render(KeysBackupBanner.State.Hidden, false) } } views.homeKeysBackupBanner.delegate = this @@ -309,10 +328,12 @@ class HomeDetailFragment @Inject constructor( views.homeToolbarContent.debouncedClicks { withState(viewModel) { - val currentSpace = it.spaceSummary.orNull() - ?.takeIf { it.roomId != ALL_COMMUNITIES_GROUP_ID } - if (vectorPreferences.labSpaces() && currentSpace != null) { - sharedActionViewModel.post(HomeActivitySharedAction.ShowSpaceSettings(currentSpace.roomId)) + if (vectorPreferences.labSpaces()) { + val currentSpace = it.spaceSummary.orNull() + ?.takeIf { it.roomId != ALL_COMMUNITIES_GROUP_ID } + if (vectorPreferences.labSpaces() && currentSpace != null) { + sharedActionViewModel.post(HomeActivitySharedAction.ShowSpaceSettings(currentSpace.roomId)) + } } } } @@ -323,7 +344,7 @@ class HomeDetailFragment @Inject constructor( views.bottomNavigationView.setOnNavigationItemSelectedListener { val displayMode = when (it.itemId) { R.id.bottom_action_people -> RoomListDisplayMode.PEOPLE - R.id.bottom_action_rooms -> RoomListDisplayMode.ROOMS + R.id.bottom_action_rooms -> RoomListDisplayMode.ROOMS else -> RoomListDisplayMode.NOTIFICATIONS } viewModel.handle(HomeDetailAction.SwitchDisplayMode(displayMode)) @@ -401,7 +422,7 @@ class HomeDetailFragment @Inject constructor( private fun RoomListDisplayMode.toMenuId() = when (this) { RoomListDisplayMode.PEOPLE -> R.id.bottom_action_people - RoomListDisplayMode.ROOMS -> R.id.bottom_action_rooms + RoomListDisplayMode.ROOMS -> R.id.bottom_action_rooms else -> R.id.bottom_action_notification } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt index 1f12f22b97..0cf57d272c 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt @@ -37,6 +37,7 @@ import org.matrix.android.sdk.api.query.RoomCategoryFilter import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams +import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.rx.asObservable import org.matrix.android.sdk.rx.rx import timber.log.Timber @@ -80,6 +81,12 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho observeSelectedGroupStore() observeSelectedSpaceStore() observeRoomSummaries() + + session.rx().liveUser(session.myUserId).execute { + copy( + myMatrixItem = it.invoke()?.getOrNull()?.toMatrixItem() + ) + } } override fun handle(action: HomeDetailAction) { diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt index d2ca7e9115..637182d052 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt @@ -23,10 +23,12 @@ import com.airbnb.mvrx.Uninitialized import org.matrix.android.sdk.api.session.group.model.GroupSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.sync.SyncState +import org.matrix.android.sdk.api.util.MatrixItem data class HomeDetailViewState( val groupSummary: Option = Option.empty(), val spaceSummary: Option = Option.empty(), + val myMatrixItem: MatrixItem? = null, val asyncRooms: Async> = Uninitialized, val displayMode: RoomListDisplayMode = RoomListDisplayMode.PEOPLE, val notificationCountCatchup: Int = 0, diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/GroupRoomListSectionBuilder.kt b/vector/src/main/java/im/vector/app/features/home/room/list/GroupRoomListSectionBuilder.kt new file mode 100644 index 0000000000..6448705979 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/list/GroupRoomListSectionBuilder.kt @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.list + +import androidx.annotation.StringRes +import im.vector.app.R +import im.vector.app.core.resources.StringProvider +import im.vector.app.features.grouplist.ALL_COMMUNITIES_GROUP_ID +import im.vector.app.features.grouplist.SelectedGroupDataSource +import im.vector.app.features.home.RoomListDisplayMode +import io.reactivex.disposables.Disposable +import io.reactivex.schedulers.Schedulers +import kotlinx.coroutines.CoroutineScope +import org.matrix.android.sdk.api.query.RoomCategoryFilter +import org.matrix.android.sdk.api.query.RoomTagQueryFilter +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams +import org.matrix.android.sdk.api.session.room.UpdatableLivePageResult +import org.matrix.android.sdk.api.session.room.model.Membership +import org.matrix.android.sdk.rx.asObservable + +class GroupRoomListSectionBuilder( + val session: Session, + val stringProvider: StringProvider, + val viewModelScope: CoroutineScope, + val selectedGroupDataSource: SelectedGroupDataSource, + val onDisposable: (Disposable) -> Unit, + val onUdpatable: (UpdatableLivePageResult) -> Unit +) : RoomListSectionBuilder { + + override fun buildSections(mode: RoomListDisplayMode): List { + val activeSpaceAwareQueries = mutableListOf() + val sections = mutableListOf() + val actualGroupId = selectedGroupDataSource.currentValue?.orNull() + ?.groupId?.takeIf { it != ALL_COMMUNITIES_GROUP_ID } + + when (mode) { + RoomListDisplayMode.PEOPLE -> { + // 3 sections Invites / Fav / Dms + buildPeopleSections(sections, activeSpaceAwareQueries, actualGroupId) + } + RoomListDisplayMode.ROOMS -> { + // 5 sections invites / Fav / Rooms / Low Priority / Server notice + buildRoomsSections(sections, activeSpaceAwareQueries, actualGroupId) + } + RoomListDisplayMode.FILTERED -> { + // Used when searching for rooms + withQueryParams( + { + it.memberships = Membership.activeMemberships() + }, + { qpm -> + val name = stringProvider.getString(R.string.bottom_action_rooms) + session.getFilteredPagedRoomSummariesLive(qpm) + .let { updatableFilterLivePageResult -> + onUdpatable(updatableFilterLivePageResult) + sections.add(RoomsSection(name, updatableFilterLivePageResult.livePagedList)) + } + } + ) + } + RoomListDisplayMode.NOTIFICATIONS -> { + addSection( + sections, + activeSpaceAwareQueries, + R.string.invitations_header, + true + ) { + it.memberships = listOf(Membership.INVITE) + it.roomCategoryFilter = RoomCategoryFilter.ALL + it.activeGroupId = actualGroupId + } + + addSection( + sections, + activeSpaceAwareQueries, + R.string.bottom_action_rooms, + false + ) { + it.memberships = listOf(Membership.JOIN) + it.roomCategoryFilter = RoomCategoryFilter.ONLY_WITH_NOTIFICATIONS + it.activeGroupId = actualGroupId + } + } + } + + selectedGroupDataSource.observe() + .distinctUntilChanged() + .subscribe { activeSpaceOption -> + val selectedGroupId = activeSpaceOption.orNull()?.groupId?.takeIf { it != ALL_COMMUNITIES_GROUP_ID } + activeSpaceAwareQueries.onEach { updater -> + updater.updateQuery { query -> + query.copy(activeGroupId = selectedGroupId) + } + } + }.also { + onDisposable.invoke(it) + } + return sections + } + + private fun buildRoomsSections(sections: MutableList, activeSpaceAwareQueries: MutableList, actualGroupId: String?) { + addSection( + sections, + activeSpaceAwareQueries, + R.string.invitations_header, + true + ) { + it.memberships = listOf(Membership.INVITE) + it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS + it.activeGroupId = actualGroupId + } + + addSection( + sections, + activeSpaceAwareQueries, + R.string.bottom_action_favourites, + false + ) { + it.memberships = listOf(Membership.JOIN) + it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS + it.roomTagQueryFilter = RoomTagQueryFilter(true, null, null) + it.activeGroupId = actualGroupId + } + + addSection( + sections, + activeSpaceAwareQueries, + R.string.bottom_action_rooms, + false + ) { + it.memberships = listOf(Membership.JOIN) + it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS + it.roomTagQueryFilter = RoomTagQueryFilter(false, false, false) + it.activeGroupId = actualGroupId + } + + addSection( + sections, + activeSpaceAwareQueries, + R.string.low_priority_header, + false + ) { + it.memberships = listOf(Membership.JOIN) + it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS + it.roomTagQueryFilter = RoomTagQueryFilter(null, true, null) + it.activeGroupId = actualGroupId + } + + addSection( + sections, + activeSpaceAwareQueries, + R.string.system_alerts_header, + false + ) { + it.memberships = listOf(Membership.JOIN) + it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS + it.roomTagQueryFilter = RoomTagQueryFilter(null, null, true) + it.activeGroupId = actualGroupId + } + } + + private fun buildPeopleSections( + sections: MutableList, + activeSpaceAwareQueries: MutableList, + actualGroupId: String? + ) { + addSection(sections, + activeSpaceAwareQueries, + R.string.invitations_header, + true + ) { + it.memberships = listOf(Membership.INVITE) + it.roomCategoryFilter = RoomCategoryFilter.ONLY_DM + it.activeGroupId = actualGroupId + } + + addSection( + sections, + activeSpaceAwareQueries, + R.string.bottom_action_favourites, + false + ) { + it.memberships = listOf(Membership.JOIN) + it.roomCategoryFilter = RoomCategoryFilter.ONLY_DM + it.roomTagQueryFilter = RoomTagQueryFilter(true, null, null) + it.activeGroupId = actualGroupId + } + + addSection( + sections, + activeSpaceAwareQueries, + R.string.bottom_action_people_x, + false + ) { + it.memberships = listOf(Membership.JOIN) + it.roomCategoryFilter = RoomCategoryFilter.ONLY_DM + it.roomTagQueryFilter = RoomTagQueryFilter(false, null, null) + it.activeGroupId = actualGroupId + } + } + + private fun addSection(sections: MutableList, + activeSpaceUpdaters: MutableList, + @StringRes nameRes: Int, + notifyOfLocalEcho: Boolean = false, + query: (RoomSummaryQueryParams.Builder) -> Unit) { + withQueryParams( + { query.invoke(it) }, + { roomQueryParams -> + + val name = stringProvider.getString(nameRes) + session.getFilteredPagedRoomSummariesLive(roomQueryParams) + .also { + activeSpaceUpdaters.add(it) + }.livePagedList + .let { livePagedList -> + // use it also as a source to update count + livePagedList.asObservable() + .observeOn(Schedulers.computation()) + .subscribe { + sections.find { it.sectionName == name } + ?.notificationCount + ?.postValue(session.getNotificationCountForRooms(roomQueryParams)) + }.also { + onDisposable.invoke(it) + } + sections.add( + RoomsSection( + sectionName = name, + livePages = livePagedList, + notifyOfLocalEcho = notifyOfLocalEcho + ) + ) + } + } + + ) + } + + private fun withQueryParams(builder: (RoomSummaryQueryParams.Builder) -> Unit, block: (RoomSummaryQueryParams) -> Unit) { + RoomSummaryQueryParams.Builder() + .apply { builder.invoke(this) } + .build() + .let { block(it) } + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListSectionBuilder.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListSectionBuilder.kt new file mode 100644 index 0000000000..5267158000 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListSectionBuilder.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.list + +import im.vector.app.features.home.RoomListDisplayMode + +interface RoomListSectionBuilder { + fun buildSections(mode: RoomListDisplayMode) : List +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt index 373bf80136..a58bee709b 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt @@ -16,9 +16,7 @@ package im.vector.app.features.home.room.list -import androidx.annotation.StringRes import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.liveData import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail @@ -28,33 +26,20 @@ import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import im.vector.app.AppStateHandler -import im.vector.app.R import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider -import im.vector.app.features.home.RoomListDisplayMode -import io.reactivex.Observable -import io.reactivex.rxkotlin.Observables -import io.reactivex.schedulers.Schedulers +import im.vector.app.features.grouplist.SelectedGroupDataSource +import im.vector.app.features.settings.VectorPreferences import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import org.matrix.android.sdk.api.MatrixPatterns import org.matrix.android.sdk.api.extensions.orFalse -import org.matrix.android.sdk.api.extensions.tryOrNull -import org.matrix.android.sdk.api.query.ActiveSpaceFilter import org.matrix.android.sdk.api.query.QueryStringValue -import org.matrix.android.sdk.api.query.RoomCategoryFilter -import org.matrix.android.sdk.api.query.RoomTagQueryFilter import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams import org.matrix.android.sdk.api.session.room.UpdatableLivePageResult import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState -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.tag.RoomTag -import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.api.session.room.state.isPublic -import org.matrix.android.sdk.rx.asObservable import org.matrix.android.sdk.rx.rx import timber.log.Timber import javax.inject.Inject @@ -63,7 +48,9 @@ class RoomListViewModel @Inject constructor( initialState: RoomListViewState, private val session: Session, private val stringProvider: StringProvider, - private val appStateHandler: AppStateHandler + private val appStateHandler: AppStateHandler, + private val selectedGroupDataSource: SelectedGroupDataSource, + private val vectorPreferences: VectorPreferences ) : VectorViewModel(initialState) { interface Factory { @@ -72,9 +59,7 @@ class RoomListViewModel @Inject constructor( private var updatableQuery: UpdatableLivePageResult? = null - private var activeSpaceAwareQueries: List? = null - - val suggestedRoomJoiningState: MutableLiveData>> = MutableLiveData(emptyMap()) + private val suggestedRoomJoiningState: MutableLiveData>> = MutableLiveData(emptyMap()) interface ActiveSpaceQueryUpdater { fun updateForSpaceId(roomId: String?) @@ -90,17 +75,6 @@ class RoomListViewModel @Inject constructor( observeMembershipChanges() appStateHandler.selectedSpaceDataSource.observe() -// .observeOn(Schedulers.computation()) - .distinctUntilChanged() - .subscribe { activeSpaceOption -> - val selectedSpace = activeSpaceOption.orNull() - activeSpaceAwareQueries?.onEach { updater -> - updater.updateForSpaceId(selectedSpace?.roomId?.takeIf { MatrixPatterns.isRoomId(it) }) - } - }.disposeOnClear() - - appStateHandler.selectedSpaceDataSource.observe() -// .observeOn(Schedulers.computation()) .distinctUntilChanged() .map { it.orNull() } .distinctUntilChanged() @@ -139,210 +113,34 @@ class RoomListViewModel @Inject constructor( } val sections: List by lazy { - val sections = mutableListOf() - val activeSpaceAwareQueries = mutableListOf() - if (initialState.displayMode == RoomListDisplayMode.PEOPLE) { - addSection(sections, - activeSpaceAwareQueries, - R.string.invitations_header, - true, - SpaceFilterStrategy.NOT_IF_ALL - ) { - it.memberships = listOf(Membership.INVITE) - it.roomCategoryFilter = RoomCategoryFilter.ONLY_DM - } - - addSection(sections, - activeSpaceAwareQueries, - R.string.bottom_action_favourites, - false, - SpaceFilterStrategy.NOT_IF_ALL - ) { - it.memberships = listOf(Membership.JOIN) - it.roomCategoryFilter = RoomCategoryFilter.ONLY_DM - it.roomTagQueryFilter = RoomTagQueryFilter(true, null, null) - } - - // For DMs we still need some post query filter :/ - // It's probably less important as home is not filtering at all - val dmList = MutableLiveData>() - Observables.combineLatest( - session.getRoomSummariesLive( - roomSummaryQueryParams { - memberships = listOf(Membership.JOIN) - roomCategoryFilter = RoomCategoryFilter.ONLY_DM - } - ).asObservable(), - appStateHandler.selectedSpaceDataSource.observe() - - ) { rooms, currentSpaceOption -> - val currentSpace = currentSpaceOption.orNull() - .takeIf { - // the +ALL trick is annoying, should find a way to fix that at the source! - MatrixPatterns.isRoomId(it?.roomId) - } - if (currentSpace == null) { - rooms - } else { - rooms.filter { - it.otherMemberIds - .intersect(currentSpace.otherMemberIds) - .isNotEmpty() - } - } - }.subscribe { - dmList.postValue(it) - }.disposeOnClear() - - sections.add( - RoomsSection( - sectionName = stringProvider.getString(R.string.bottom_action_people_x), - liveList = dmList, - notifyOfLocalEcho = false - ) - ) - } else if (initialState.displayMode == RoomListDisplayMode.ROOMS) { - addSection( - sections, activeSpaceAwareQueries, - R.string.invitations_header, - true, - SpaceFilterStrategy.NONE - ) { - it.memberships = listOf(Membership.INVITE) - it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS - } - - addSection( - sections, - activeSpaceAwareQueries, - R.string.bottom_action_favourites, - false, - SpaceFilterStrategy.NOT_IF_ALL - ) { - it.memberships = listOf(Membership.JOIN) - it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS - it.roomTagQueryFilter = RoomTagQueryFilter(true, null, null) - } - - addSection( - sections, - activeSpaceAwareQueries, - R.string.bottom_action_rooms, - false, - SpaceFilterStrategy.NORMAL - ) { - it.memberships = listOf(Membership.JOIN) - it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS - it.roomTagQueryFilter = RoomTagQueryFilter(false, false, false) - } - - addSection( - sections, - activeSpaceAwareQueries, - R.string.low_priority_header, - false, - SpaceFilterStrategy.NORMAL - ) { - it.memberships = listOf(Membership.JOIN) - it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS - it.roomTagQueryFilter = RoomTagQueryFilter(null, true, null) - } - - addSection( - sections, - activeSpaceAwareQueries, - R.string.system_alerts_header, - false, - SpaceFilterStrategy.NORMAL - ) { - it.memberships = listOf(Membership.JOIN) - it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS - it.roomTagQueryFilter = RoomTagQueryFilter(null, null, true) - } - - // add suggested rooms - val suggestedRoomsObservable = // MutableLiveData>() - appStateHandler.selectedSpaceDataSource.observe() - .distinctUntilChanged() - .switchMap { activeSpaceOption -> - val selectedSpace = activeSpaceOption.orNull() - if (selectedSpace == null) { - Observable.just(emptyList()) - } else { - liveData(context = viewModelScope.coroutineContext + Dispatchers.IO) { - val spaceSum = tryOrNull { session.spaceService().querySpaceChildren(selectedSpace.roomId, suggestedOnly = true) } - val value = spaceSum?.second ?: emptyList() - // i need to check if it's already joined. - val filtered = value.filter { - session.getRoomSummary(it.childRoomId)?.membership?.isActive() != true - } - emit(filtered) - }.asObservable() - } - } -// .subscribe { -// Timber.w("VAL: Suggested rooms is ${it}") -// liveSuggestedRooms.postValue(it) -// }.disposeOnClear() - - val liveSuggestedRooms = MutableLiveData() - Observables.combineLatest( - suggestedRoomsObservable, - suggestedRoomJoiningState.asObservable() - ) { rooms, joinStates -> - SuggestedRoomInfo( - rooms, - joinStates - ) - }.subscribe { - liveSuggestedRooms.postValue(it) - }.disposeOnClear() - sections.add( - RoomsSection( - sectionName = stringProvider.getString(R.string.suggested_header), - liveSuggested = liveSuggestedRooms, - notifyOfLocalEcho = false - ) - ) - } else if (initialState.displayMode == RoomListDisplayMode.FILTERED) { - withQueryParams( + if (vectorPreferences.labSpaces()) { + SpaceRoomListSectionBuilder( + session, + stringProvider, + appStateHandler, + viewModelScope, + suggestedRoomJoiningState, { - it.memberships = Membership.activeMemberships() + it.disposeOnClear() }, - { qpm -> - val name = stringProvider.getString(R.string.bottom_action_rooms) - session.getFilteredPagedRoomSummariesLive(qpm) - .let { updatableFilterLivePageResult -> - updatableQuery = updatableFilterLivePageResult - sections.add(RoomsSection(name, updatableFilterLivePageResult.livePagedList)) - } + { + updatableQuery = it } - ) - } else if (initialState.displayMode == RoomListDisplayMode.NOTIFICATIONS) { - addSection( - sections, - activeSpaceAwareQueries, - R.string.invitations_header, - true, - SpaceFilterStrategy.NORMAL - ) { - it.memberships = listOf(Membership.INVITE) - it.roomCategoryFilter = RoomCategoryFilter.ALL - } - - addSection( - sections, - activeSpaceAwareQueries, - R.string.bottom_action_rooms, - false, - SpaceFilterStrategy.NORMAL - ) { - it.memberships = listOf(Membership.JOIN) - it.roomCategoryFilter = RoomCategoryFilter.ONLY_WITH_NOTIFICATIONS - } + ).buildSections(initialState.displayMode) + } else { + GroupRoomListSectionBuilder( + session, + stringProvider, + viewModelScope, + selectedGroupDataSource, + { + it.disposeOnClear() + }, + { + updatableQuery = it + } + ).buildSections(initialState.displayMode) } - - sections } override fun handle(action: RoomListAction) { @@ -359,100 +157,6 @@ class RoomListViewModel @Inject constructor( }.exhaustive } - private fun addSection(sections: MutableList, - activeSpaceUpdaters: MutableList, - @StringRes nameRes: Int, - notifyOfLocalEcho: Boolean = false, - spaceFilterStrategy: SpaceFilterStrategy = SpaceFilterStrategy.NONE, - query: (RoomSummaryQueryParams.Builder) -> Unit) { - withQueryParams( - { query.invoke(it) }, - { roomQueryParams -> - - val name = stringProvider.getString(nameRes) -// if (activeSpaceAwareQueries != null) { - session.getFilteredPagedRoomSummariesLive( - when (spaceFilterStrategy) { - SpaceFilterStrategy.NORMAL -> { - roomQueryParams.copy( - activeSpaceId = ActiveSpaceFilter.ActiveSpace(appStateHandler.safeActiveSpaceId()) - ) - } - SpaceFilterStrategy.NOT_IF_ALL -> { - if (appStateHandler.safeActiveSpaceId() == null) { - roomQueryParams - } else { - roomQueryParams.copy( - activeSpaceId = ActiveSpaceFilter.ActiveSpace(appStateHandler.safeActiveSpaceId()) - ) - } - } - SpaceFilterStrategy.NONE -> roomQueryParams - } - - ).also { - when (spaceFilterStrategy) { - SpaceFilterStrategy.NORMAL -> { - activeSpaceUpdaters.add(object : ActiveSpaceQueryUpdater { - override fun updateForSpaceId(roomId: String?) { - it.updateQuery { - it.copy( - activeSpaceId = ActiveSpaceFilter.ActiveSpace(roomId) - ) - } - } - }) - } - SpaceFilterStrategy.NOT_IF_ALL -> { - activeSpaceUpdaters.add(object : ActiveSpaceQueryUpdater { - override fun updateForSpaceId(roomId: String?) { - if (roomId != null) { - it.updateQuery { - it.copy( - activeSpaceId = ActiveSpaceFilter.ActiveSpace(roomId) - ) - } - } - } - }) - } - SpaceFilterStrategy.NONE -> { - // we ignore current space for this one - } - } - }.livePagedList - .let { livePagedList -> - - // use it also as a source to update count - livePagedList.asObservable() - .observeOn(Schedulers.computation()) - .subscribe { - sections.find { it.sectionName == name } - ?.notificationCount - ?.postValue(session.getNotificationCountForRooms(roomQueryParams)) - } - .disposeOnClear() - - sections.add( - RoomsSection( - sectionName = name, - livePages = livePagedList, - notifyOfLocalEcho = notifyOfLocalEcho - ) - ) - } - } - - ) - } - - private fun withQueryParams(builder: (RoomSummaryQueryParams.Builder) -> Unit, block: (RoomSummaryQueryParams) -> Unit) { - RoomSummaryQueryParams.Builder() - .apply { builder.invoke(this) } - .build() - .let { block(it) } - } - fun isPublicRoom(roomId: String): Boolean { return session.getRoom(roomId)?.isPublic().orFalse() } @@ -604,7 +308,7 @@ class RoomListViewModel @Inject constructor( private fun String.otherTag(): String? { return when (this) { - RoomTag.ROOM_TAG_FAVOURITE -> RoomTag.ROOM_TAG_LOW_PRIORITY + RoomTag.ROOM_TAG_FAVOURITE -> RoomTag.ROOM_TAG_LOW_PRIORITY RoomTag.ROOM_TAG_LOW_PRIORITY -> RoomTag.ROOM_TAG_FAVOURITE else -> null } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModelFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModelFactory.kt index 8377f222bf..0a142304a0 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModelFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModelFactory.kt @@ -18,13 +18,17 @@ package im.vector.app.features.home.room.list import im.vector.app.AppStateHandler import im.vector.app.core.resources.StringProvider +import im.vector.app.features.grouplist.SelectedGroupDataSource +import im.vector.app.features.settings.VectorPreferences import org.matrix.android.sdk.api.session.Session import javax.inject.Inject import javax.inject.Provider class RoomListViewModelFactory @Inject constructor(private val session: Provider, private val appStateHandler: AppStateHandler, - private val stringProvider: StringProvider) + private val stringProvider: StringProvider, + private val vectorPreferences: VectorPreferences, + private val selectedGroupDataSource: SelectedGroupDataSource) : RoomListViewModel.Factory { override fun create(initialState: RoomListViewState): RoomListViewModel { @@ -32,7 +36,9 @@ class RoomListViewModelFactory @Inject constructor(private val session: Provider initialState, session.get(), stringProvider, - appStateHandler + appStateHandler, + selectedGroupDataSource, + vectorPreferences ) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/SpaceRoomListSectionBuilder.kt b/vector/src/main/java/im/vector/app/features/home/room/list/SpaceRoomListSectionBuilder.kt new file mode 100644 index 0000000000..49be503c80 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/list/SpaceRoomListSectionBuilder.kt @@ -0,0 +1,381 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.list + +import androidx.annotation.StringRes +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.liveData +import com.airbnb.mvrx.Async +import im.vector.app.AppStateHandler +import im.vector.app.R +import im.vector.app.core.resources.StringProvider +import im.vector.app.features.home.RoomListDisplayMode +import io.reactivex.Observable +import io.reactivex.disposables.Disposable +import io.reactivex.rxkotlin.Observables +import io.reactivex.schedulers.Schedulers +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import org.matrix.android.sdk.api.MatrixPatterns +import org.matrix.android.sdk.api.extensions.tryOrNull +import org.matrix.android.sdk.api.query.ActiveSpaceFilter +import org.matrix.android.sdk.api.query.RoomCategoryFilter +import org.matrix.android.sdk.api.query.RoomTagQueryFilter +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams +import org.matrix.android.sdk.api.session.room.UpdatableLivePageResult +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.roomSummaryQueryParams +import org.matrix.android.sdk.rx.asObservable + +class SpaceRoomListSectionBuilder( + val session: Session, + val stringProvider: StringProvider, + val appStateHandler: AppStateHandler, + val viewModelScope: CoroutineScope, + private val suggestedRoomJoiningState: LiveData>>, + val onDisposable: (Disposable) -> Unit, + val onUdpatable: (UpdatableLivePageResult) -> Unit +) : RoomListSectionBuilder { + + override fun buildSections(mode: RoomListDisplayMode): List { + val sections = mutableListOf() + val activeSpaceAwareQueries = mutableListOf() + when (mode) { + RoomListDisplayMode.PEOPLE -> { + buildDmSections(sections, activeSpaceAwareQueries) + } + RoomListDisplayMode.ROOMS -> { + buildRoomsSections(sections, activeSpaceAwareQueries) + } + RoomListDisplayMode.FILTERED -> { + withQueryParams( + { + it.memberships = Membership.activeMemberships() + }, + { qpm -> + val name = stringProvider.getString(R.string.bottom_action_rooms) + session.getFilteredPagedRoomSummariesLive(qpm) + .let { updatableFilterLivePageResult -> + onUdpatable(updatableFilterLivePageResult) + sections.add(RoomsSection(name, updatableFilterLivePageResult.livePagedList)) + } + } + ) + } + RoomListDisplayMode.NOTIFICATIONS -> { + addSection( + sections, + activeSpaceAwareQueries, + R.string.invitations_header, + true, + RoomListViewModel.SpaceFilterStrategy.NORMAL + ) { + it.memberships = listOf(Membership.INVITE) + it.roomCategoryFilter = RoomCategoryFilter.ALL + } + + addSection( + sections, + activeSpaceAwareQueries, + R.string.bottom_action_rooms, + false, + RoomListViewModel.SpaceFilterStrategy.NORMAL + ) { + it.memberships = listOf(Membership.JOIN) + it.roomCategoryFilter = RoomCategoryFilter.ONLY_WITH_NOTIFICATIONS + } + } + } + + appStateHandler.selectedSpaceDataSource.observe() + .distinctUntilChanged() + .subscribe { activeSpaceOption -> + val selectedSpace = activeSpaceOption.orNull() + activeSpaceAwareQueries.onEach { updater -> + updater.updateForSpaceId(selectedSpace?.roomId?.takeIf { MatrixPatterns.isRoomId(it) }) + } + }.also { + onDisposable.invoke(it) + } + + return sections + } + + private fun buildRoomsSections(sections: MutableList, activeSpaceAwareQueries: MutableList) { + addSection( + sections, activeSpaceAwareQueries, + R.string.invitations_header, + true, + RoomListViewModel.SpaceFilterStrategy.NONE + ) { + it.memberships = listOf(Membership.INVITE) + it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS + } + + addSection( + sections, + activeSpaceAwareQueries, + R.string.bottom_action_favourites, + false, + RoomListViewModel.SpaceFilterStrategy.NOT_IF_ALL + ) { + it.memberships = listOf(Membership.JOIN) + it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS + it.roomTagQueryFilter = RoomTagQueryFilter(true, null, null) + } + + addSection( + sections, + activeSpaceAwareQueries, + R.string.bottom_action_rooms, + false, + RoomListViewModel.SpaceFilterStrategy.NORMAL + ) { + it.memberships = listOf(Membership.JOIN) + it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS + it.roomTagQueryFilter = RoomTagQueryFilter(false, false, false) + } + + addSection( + sections, + activeSpaceAwareQueries, + R.string.low_priority_header, + false, + RoomListViewModel.SpaceFilterStrategy.NORMAL + ) { + it.memberships = listOf(Membership.JOIN) + it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS + it.roomTagQueryFilter = RoomTagQueryFilter(null, true, null) + } + + addSection( + sections, + activeSpaceAwareQueries, + R.string.system_alerts_header, + false, + RoomListViewModel.SpaceFilterStrategy.NORMAL + ) { + it.memberships = listOf(Membership.JOIN) + it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS + it.roomTagQueryFilter = RoomTagQueryFilter(null, null, true) + } + + // add suggested rooms + val suggestedRoomsObservable = // MutableLiveData>() + appStateHandler.selectedSpaceDataSource.observe() + .distinctUntilChanged() + .switchMap { activeSpaceOption -> + val selectedSpace = activeSpaceOption.orNull() + if (selectedSpace == null) { + Observable.just(emptyList()) + } else { + liveData(context = viewModelScope.coroutineContext + Dispatchers.IO) { + val spaceSum = tryOrNull { session.spaceService().querySpaceChildren(selectedSpace.roomId, suggestedOnly = true) } + val value = spaceSum?.second ?: emptyList() + // i need to check if it's already joined. + val filtered = value.filter { + session.getRoomSummary(it.childRoomId)?.membership?.isActive() != true + } + emit(filtered) + }.asObservable() + } + } + + val liveSuggestedRooms = MutableLiveData() + Observables.combineLatest( + suggestedRoomsObservable, + suggestedRoomJoiningState.asObservable() + ) { rooms, joinStates -> + SuggestedRoomInfo( + rooms, + joinStates + ) + }.subscribe { + liveSuggestedRooms.postValue(it) + }.also { + onDisposable.invoke(it) + } + sections.add( + RoomsSection( + sectionName = stringProvider.getString(R.string.suggested_header), + liveSuggested = liveSuggestedRooms, + notifyOfLocalEcho = false + ) + ) + } + + private fun buildDmSections(sections: MutableList, activeSpaceAwareQueries: MutableList) { + addSection(sections, + activeSpaceAwareQueries, + R.string.invitations_header, + true, + RoomListViewModel.SpaceFilterStrategy.NOT_IF_ALL + ) { + it.memberships = listOf(Membership.INVITE) + it.roomCategoryFilter = RoomCategoryFilter.ONLY_DM + } + + addSection(sections, + activeSpaceAwareQueries, + R.string.bottom_action_favourites, + false, + RoomListViewModel.SpaceFilterStrategy.NOT_IF_ALL + ) { + it.memberships = listOf(Membership.JOIN) + it.roomCategoryFilter = RoomCategoryFilter.ONLY_DM + it.roomTagQueryFilter = RoomTagQueryFilter(true, null, null) + } + + // For DMs we still need some post query filter :/ + // It's probably less important as home is not filtering at all + val dmList = MutableLiveData>() + Observables.combineLatest( + session.getRoomSummariesLive( + roomSummaryQueryParams { + memberships = listOf(Membership.JOIN) + roomCategoryFilter = RoomCategoryFilter.ONLY_DM + } + ).asObservable(), + appStateHandler.selectedSpaceDataSource.observe() + + ) { rooms, currentSpaceOption -> + val currentSpace = currentSpaceOption.orNull() + .takeIf { + // the +ALL trick is annoying, should find a way to fix that at the source! + MatrixPatterns.isRoomId(it?.roomId) + } + if (currentSpace == null) { + rooms + } else { + rooms.filter { + it.otherMemberIds + .intersect(currentSpace.otherMemberIds) + .isNotEmpty() + } + } + }.subscribe { + dmList.postValue(it) + }.also { + onDisposable.invoke(it) + } + + sections.add( + RoomsSection( + sectionName = stringProvider.getString(R.string.bottom_action_people_x), + liveList = dmList, + notifyOfLocalEcho = false + ) + ) + } + + private fun addSection(sections: MutableList, + activeSpaceUpdaters: MutableList, + @StringRes nameRes: Int, + notifyOfLocalEcho: Boolean = false, + spaceFilterStrategy: RoomListViewModel.SpaceFilterStrategy = RoomListViewModel.SpaceFilterStrategy.NONE, + query: (RoomSummaryQueryParams.Builder) -> Unit) { + withQueryParams( + { query.invoke(it) }, + { roomQueryParams -> + + val name = stringProvider.getString(nameRes) + session.getFilteredPagedRoomSummariesLive( + when (spaceFilterStrategy) { + RoomListViewModel.SpaceFilterStrategy.NORMAL -> { + roomQueryParams.copy( + activeSpaceId = ActiveSpaceFilter.ActiveSpace(appStateHandler.safeActiveSpaceId()) + ) + } + RoomListViewModel.SpaceFilterStrategy.NOT_IF_ALL -> { + if (appStateHandler.safeActiveSpaceId() == null) { + roomQueryParams + } else { + roomQueryParams.copy( + activeSpaceId = ActiveSpaceFilter.ActiveSpace(appStateHandler.safeActiveSpaceId()) + ) + } + } + RoomListViewModel.SpaceFilterStrategy.NONE -> roomQueryParams + } + + ).also { + when (spaceFilterStrategy) { + RoomListViewModel.SpaceFilterStrategy.NORMAL -> { + activeSpaceUpdaters.add(object : RoomListViewModel.ActiveSpaceQueryUpdater { + override fun updateForSpaceId(roomId: String?) { + it.updateQuery { + it.copy( + activeSpaceId = ActiveSpaceFilter.ActiveSpace(roomId) + ) + } + } + }) + } + RoomListViewModel.SpaceFilterStrategy.NOT_IF_ALL -> { + activeSpaceUpdaters.add(object : RoomListViewModel.ActiveSpaceQueryUpdater { + override fun updateForSpaceId(roomId: String?) { + if (roomId != null) { + it.updateQuery { + it.copy( + activeSpaceId = ActiveSpaceFilter.ActiveSpace(roomId) + ) + } + } + } + }) + } + RoomListViewModel.SpaceFilterStrategy.NONE -> { + // we ignore current space for this one + } + } + }.livePagedList + .let { livePagedList -> + + // use it also as a source to update count + livePagedList.asObservable() + .observeOn(Schedulers.computation()) + .subscribe { + sections.find { it.sectionName == name } + ?.notificationCount + ?.postValue(session.getNotificationCountForRooms(roomQueryParams)) + }.also { + onDisposable.invoke(it) + } + + sections.add( + RoomsSection( + sectionName = name, + livePages = livePagedList, + notifyOfLocalEcho = notifyOfLocalEcho + ) + ) + } + } + + ) + } + + private fun withQueryParams(builder: (RoomSummaryQueryParams.Builder) -> Unit, block: (RoomSummaryQueryParams) -> Unit) { + RoomSummaryQueryParams.Builder() + .apply { builder.invoke(this) } + .build() + .let { block(it) } + } +}