From 733d4185c491bd4860ba2f02dc3a6695a78e1346 Mon Sep 17 00:00:00 2001 From: Valere Date: Wed, 21 Apr 2021 15:33:32 +0200 Subject: [PATCH] Fix / Add unread count in hamburger menu --- .../vector/app/features/home/HomeActivity.kt | 6 + .../app/features/home/HomeDetailFragment.kt | 15 ++ .../home/UnreadMessagesSharedViewModel.kt | 152 ++++++++++++++++++ .../features/spaces/SpacesListViewModel.kt | 4 +- .../main/res/layout/fragment_home_detail.xml | 36 ++--- 5 files changed, 194 insertions(+), 19 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt index cfdcc40a37..24ad6c8b85 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt @@ -85,6 +85,7 @@ class HomeActivity : ToolbarConfigurable, UnknownDeviceDetectorSharedViewModel.Factory, ServerBackupStatusViewModel.Factory, + UnreadMessagesSharedViewModel.Factory, NavigationInterceptor { private lateinit var sharedActionViewModel: HomeSharedActionViewModel @@ -103,6 +104,7 @@ class HomeActivity : @Inject lateinit var popupAlertManager: PopupAlertManager @Inject lateinit var shortcutsHandler: ShortcutsHandler @Inject lateinit var unknownDeviceViewModelFactory: UnknownDeviceDetectorSharedViewModel.Factory + @Inject lateinit var unreadMessagesSharedViewModelFactory: UnreadMessagesSharedViewModel.Factory @Inject lateinit var permalinkHandler: PermalinkHandler @Inject lateinit var avatarRenderer: AvatarRenderer @Inject lateinit var initSyncStepFormatter: InitSyncStepFormatter @@ -140,6 +142,10 @@ class HomeActivity : return serverBackupviewModelFactory.create(initialState) } + override fun create(initialState: UnreadMessagesState): UnreadMessagesSharedViewModel { + return unreadMessagesSharedViewModelFactory.create(initialState) + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) FcmHelper.ensureFcmTokenIsRetrieved(this, pushManager, vectorPreferences.areNotificationEnabledForDevice()) 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 36bf32919c..e7831c72cc 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 @@ -43,6 +43,7 @@ import im.vector.app.features.call.VectorCallActivity import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.home.room.list.RoomListFragment import im.vector.app.features.home.room.list.RoomListParams +import im.vector.app.features.home.room.list.UnreadCounterBadgeView import im.vector.app.features.popup.PopupAlertManager import im.vector.app.features.popup.VerificationVectorAlert import im.vector.app.features.settings.VectorPreferences @@ -71,6 +72,7 @@ class HomeDetailFragment @Inject constructor( private val viewModel: HomeDetailViewModel by fragmentViewModel() private val unknownDeviceDetectorSharedViewModel: UnknownDeviceDetectorSharedViewModel by activityViewModel() + private val unreadMessagesSharedViewModel: UnreadMessagesSharedViewModel by activityViewModel() private val serverBackupStatusViewModel: ServerBackupStatusViewModel by activityViewModel() private lateinit var sharedActionViewModel: HomeSharedActionViewModel @@ -157,6 +159,19 @@ class HomeDetailFragment @Inject constructor( } } + unreadMessagesSharedViewModel.subscribe { state -> + if (vectorPreferences.labSpaces()) { + views.drawerUnreadCounterBadgeView.render( + UnreadCounterBadgeView.State( + count = state.otherSpacesUnread.totalCount, + highlighted = state.otherSpacesUnread.isHighlight + ) + ) + } else { + views.drawerUnreadCounterBadgeView.isVisible = false + } + } + sharedCallActionViewModel .liveKnownCalls .observe(viewLifecycleOwner, { diff --git a/vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt new file mode 100644 index 0000000000..85440c6582 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt @@ -0,0 +1,152 @@ +/* + * 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 + +import com.airbnb.mvrx.ActivityViewModelContext +import com.airbnb.mvrx.FragmentViewModelContext +import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.ViewModelContext +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import im.vector.app.AppStateHandler +import im.vector.app.core.platform.EmptyAction +import im.vector.app.core.platform.EmptyViewEvents +import im.vector.app.core.platform.VectorViewModel +import im.vector.app.features.settings.VectorPreferences +import io.reactivex.Observable +import io.reactivex.functions.BiFunction +import io.reactivex.schedulers.Schedulers +import org.matrix.android.sdk.api.query.ActiveSpaceFilter +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.room.RoomSortOrder +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.session.room.summary.RoomAggregateNotificationCount +import org.matrix.android.sdk.rx.asObservable +import java.util.concurrent.TimeUnit + +data class UnreadMessagesState( + val homeSpaceUnread: RoomAggregateNotificationCount = RoomAggregateNotificationCount(0, 0), + val otherSpacesUnread: RoomAggregateNotificationCount = RoomAggregateNotificationCount(0, 0) +) : MvRxState + +data class CountInfo( + val homeCount: RoomAggregateNotificationCount, + val otherCount: RoomAggregateNotificationCount +) + +class UnreadMessagesSharedViewModel @AssistedInject constructor(@Assisted initialState: UnreadMessagesState, + session: Session, + appStateHandler: AppStateHandler, + private val vectorPreferences: VectorPreferences) + : VectorViewModel(initialState) { + + @AssistedFactory + interface Factory { + fun create(initialState: UnreadMessagesState): UnreadMessagesSharedViewModel + } + + companion object : MvRxViewModelFactory { + + @JvmStatic + override fun create(viewModelContext: ViewModelContext, state: UnreadMessagesState): UnreadMessagesSharedViewModel? { + val factory = when (viewModelContext) { + is FragmentViewModelContext -> viewModelContext.fragment as? UnreadMessagesSharedViewModel.Factory + is ActivityViewModelContext -> viewModelContext.activity as? UnreadMessagesSharedViewModel.Factory + } + return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") + } + } + + override fun handle(action: EmptyAction) {} + + init { + + session.getPagedRoomSummariesLive( + roomSummaryQueryParams { + this.memberships = listOf(Membership.JOIN) + this.activeSpaceId = ActiveSpaceFilter.ActiveSpace(null) + }, sortOrder = RoomSortOrder.NONE + ).asObservable() + .throttleFirst(300, TimeUnit.MILLISECONDS) + .subscribe { + val counts = session.getNotificationCountForRooms( + roomSummaryQueryParams { + this.memberships = listOf(Membership.JOIN) + this.activeSpaceId = ActiveSpaceFilter.ActiveSpace(null) + } + ) + val invites = session.getRoomSummaries(roomSummaryQueryParams { + this.memberships = listOf(Membership.INVITE) + this.activeSpaceId = ActiveSpaceFilter.ActiveSpace(null) + }).size + setState { + copy( + homeSpaceUnread = RoomAggregateNotificationCount( + counts.notificationCount + invites, + highlightCount = counts.highlightCount + invites + ) + ) + } + }.disposeOnClear() + + Observable.combineLatest( + appStateHandler.selectedSpaceObservable.distinctUntilChanged(), + appStateHandler.selectedSpaceObservable.switchMap { + session.getPagedRoomSummariesLive( + roomSummaryQueryParams { + this.memberships = Membership.activeMemberships() + }, sortOrder = RoomSortOrder.NONE + ).asObservable() + .throttleFirst(300, TimeUnit.MILLISECONDS) + .observeOn(Schedulers.computation()) + }, + BiFunction { _, _ -> + val selectedSpace = appStateHandler.safeActiveSpaceId() + val counts = session.getNotificationCountForRooms( + roomSummaryQueryParams { + this.memberships = listOf(Membership.JOIN) + this.activeSpaceId = ActiveSpaceFilter.ActiveSpace(null) + } + ) + val rootCounts = session.spaceService().getRootSpaceSummaries() + .filter { + // filter out current selection + it.roomId != selectedSpace + } + CountInfo( + homeCount = counts, + otherCount = RoomAggregateNotificationCount( + rootCounts.fold(0, { acc, rs -> + acc + rs.notificationCount + }) + (counts.notificationCount.takeIf { selectedSpace != null } ?: 0), + rootCounts.fold(0, { acc, rs -> + acc + rs.highlightCount + }) + (counts.highlightCount.takeIf { selectedSpace != null } ?: 0) + ) + ) + } + ).execute { + copy( + homeSpaceUnread = it.invoke()?.homeCount ?: RoomAggregateNotificationCount(0, 0), + otherSpacesUnread = it.invoke()?.otherCount ?: RoomAggregateNotificationCount(0, 0) + ) + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt index 1671be5ffb..de8e8bbc95 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt @@ -30,6 +30,7 @@ import im.vector.app.core.resources.StringProvider import im.vector.app.features.ui.UiStateRepository import io.reactivex.Observable import io.reactivex.functions.BiFunction +import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.launch import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.query.ActiveSpaceFilter @@ -90,6 +91,7 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp }, sortOrder = RoomSortOrder.NONE ).asObservable() .throttleFirst(300, TimeUnit.MILLISECONDS) + .observeOn(Schedulers.computation()) .subscribe { val counts = session.getNotificationCountForRooms( roomSummaryQueryParams { @@ -168,7 +170,7 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp } private fun observeSpaceSummaries() { - val spaceSummaryQueryParams = roomSummaryQueryParams() { + val spaceSummaryQueryParams = roomSummaryQueryParams { memberships = listOf(Membership.JOIN, Membership.INVITE) displayName = QueryStringValue.IsNotEmpty excludeType = listOf(/**RoomType.MESSAGING,$*/ diff --git a/vector/src/main/res/layout/fragment_home_detail.xml b/vector/src/main/res/layout/fragment_home_detail.xml index 419ba5b64f..e6ecdd9aa8 100644 --- a/vector/src/main/res/layout/fragment_home_detail.xml +++ b/vector/src/main/res/layout/fragment_home_detail.xml @@ -44,24 +44,24 @@ /> - - - - - - - - - - - - - - - - - - +