From d30ba9c7491b332b76c701edebb83a90079954b0 Mon Sep 17 00:00:00 2001 From: Valere Date: Tue, 20 Apr 2021 10:29:44 +0200 Subject: [PATCH] Spaces quick fixes and updates Make drawer menu more obvious + fix notification count --- CHANGES.md | 2 +- .../summary/RoomAggregateNotificationCount.kt | 2 +- .../room/summary/RoomSummaryUpdater.kt | 23 ++++++++ .../grouplist/HomeSpaceSummaryItem.kt | 5 ++ .../app/features/home/HomeDetailFragment.kt | 33 ------------ .../app/features/home/HomeDetailViewModel.kt | 4 +- .../room/list/SpaceRoomListSectionBuilder.kt | 42 ++++++++------- .../app/features/spaces/SpaceListViewState.kt | 4 +- .../features/spaces/SpaceSummaryController.kt | 54 +++++++++++-------- .../app/features/spaces/SpaceSummaryItem.kt | 4 ++ .../features/spaces/SpacesListViewModel.kt | 25 +++++++++ .../src/main/res/drawable/ic_space_icons.xml | 27 ++++++++++ .../main/res/layout/fragment_home_detail.xml | 47 +++++++++++++--- .../main/res/layout/item_room_placeholder.xml | 2 +- vector/src/main/res/layout/item_space.xml | 22 ++++++++ 15 files changed, 211 insertions(+), 85 deletions(-) create mode 100644 vector/src/main/res/drawable/ic_space_icons.xml diff --git a/CHANGES.md b/CHANGES.md index b447b6e6e5..a99b4baf68 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,7 +2,7 @@ Changes in Element 1.1.7 (2021-XX-XX) =================================================== Features ✨: - - + - Spaces beta Improvements 🙌: - diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/summary/RoomAggregateNotificationCount.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/summary/RoomAggregateNotificationCount.kt index 066178b1ec..b3440059e8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/summary/RoomAggregateNotificationCount.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/summary/RoomAggregateNotificationCount.kt @@ -20,6 +20,6 @@ data class RoomAggregateNotificationCount( val notificationCount: Int, val highlightCount: Int ) { - val totalCount = notificationCount + highlightCount + val totalCount = notificationCount val isHighlight = highlightCount > 0 } 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 dae2c0838e..56eb3db236 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 @@ -349,6 +349,29 @@ internal class RoomSummaryUpdater @Inject constructor( // Timber.v("## SPACES: flatten of ${dmRoom.otherMemberIds.joinToString(",")} is ${dmRoom.flattenParentIds}") } + // Maybe a good place to count the number of notifications for spaces? + + realm.where(RoomSummaryEntity::class.java) + .process(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.activeMemberships()) + .equalTo(RoomSummaryEntityFields.ROOM_TYPE, RoomType.SPACE) + .findAll().forEach { space -> + // get all children + var highlightCount = 0 + var notificationCount = 0 + realm.where(RoomSummaryEntity::class.java) + .process(RoomSummaryEntityFields.MEMBERSHIP_STR, listOf(Membership.JOIN)) + .notEqualTo(RoomSummaryEntityFields.ROOM_TYPE, RoomType.SPACE) + .contains(RoomSummaryEntityFields.FLATTEN_PARENT_IDS, space.roomId) + .findAll().forEach { + highlightCount += it.highlightCount + notificationCount += it.notificationCount + } + + space.highlightCount = highlightCount + space.notificationCount = notificationCount + } + // xxx invites?? + // LEGACY GROUPS // lets mark rooms that belongs to groups val existingGroups = GroupSummaryEntity.where(realm).findAll() diff --git a/vector/src/main/java/im/vector/app/features/grouplist/HomeSpaceSummaryItem.kt b/vector/src/main/java/im/vector/app/features/grouplist/HomeSpaceSummaryItem.kt index 9912564720..553f82e98f 100644 --- a/vector/src/main/java/im/vector/app/features/grouplist/HomeSpaceSummaryItem.kt +++ b/vector/src/main/java/im/vector/app/features/grouplist/HomeSpaceSummaryItem.kt @@ -28,6 +28,7 @@ import im.vector.app.R import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.platform.CheckableConstraintLayout +import im.vector.app.features.home.room.list.UnreadCounterBadgeView import im.vector.app.features.themes.ThemeUtils @EpoxyModelClass(layout = R.layout.item_space) @@ -35,6 +36,7 @@ abstract class HomeSpaceSummaryItem : VectorEpoxyModel Unit)? = null + @EpoxyAttribute var countState : UnreadCounterBadgeView.State = UnreadCounterBadgeView.State(0, false) override fun getViewType(): Int { // mm.. it's reusing the same layout for basic space item @@ -52,6 +54,8 @@ abstract class HomeSpaceSummaryItem : VectorEpoxyModel(R.id.groupNameView) val rootView by bind(R.id.itemGroupLayout) val leaveView by bind(R.id.groupTmpLeave) + val counterBadgeView by bind(R.id.groupCounterBadge) } } 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 607a1aeb54..36bf32919c 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 @@ -22,7 +22,6 @@ import android.view.Menu import android.view.MenuItem import android.view.View import android.view.ViewGroup -import android.widget.ImageView import androidx.core.content.ContextCompat import androidx.core.view.isVisible import com.airbnb.mvrx.activityViewModel @@ -32,7 +31,6 @@ import com.google.android.material.badge.BadgeDrawable import im.vector.app.R import im.vector.app.core.extensions.commitTransaction import im.vector.app.core.extensions.toMvRxBundle -import im.vector.app.core.glide.GlideApp import im.vector.app.core.platform.ToolbarConfigurable import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseFragment @@ -56,14 +54,9 @@ import im.vector.app.features.workers.signout.ServerBackupStatusViewModel import im.vector.app.features.workers.signout.ServerBackupStatusViewState 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.util.toMatrixItem import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo import javax.inject.Inject -private const val INDEX_PEOPLE = 0 -private const val INDEX_ROOMS = 1 -private const val INDEX_CATCHUP = 2 - class HomeDetailFragment @Inject constructor( val homeDetailViewModelFactory: HomeDetailViewModel.Factory, private val serverBackupStatusViewModelFactory: ServerBackupStatusViewModel.Factory, @@ -251,19 +244,8 @@ class HomeDetailFragment @Inject constructor( private fun onGroupChange(groupSummary: GroupSummary?) { 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(groupSummary.toMatrixItem(), views.groupToolbarAvatarImageView, GlideApp.with(requireActivity())) views.groupToolbarSpaceTitleView.isVisible = true views.groupToolbarSpaceTitleView.text = groupSummary.displayName } @@ -271,24 +253,9 @@ class HomeDetailFragment @Inject constructor( private fun onSpaceChange(spaceSummary: RoomSummary?) { spaceSummary ?: return - - // Use GlideApp with activity context to avoid the glideRequests to be paused if (spaceSummary.roomId == ALL_COMMUNITIES_GROUP_ID) { - // Special case - views.groupToolbarAvatarImageView.background = ContextCompat.getDrawable(requireContext(), R.drawable.space_home_background) - views.groupToolbarAvatarImageView.scaleType = ImageView.ScaleType.CENTER_INSIDE - ThemeUtils.tintDrawableWithColor( - ContextCompat.getDrawable(requireContext(), R.drawable.ic_space_home)!!, - ThemeUtils.getColor(requireContext(), R.attr.riot_primary_text_color) - ).let { - views.groupToolbarAvatarImageView.setImageDrawable(it) - } - 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 views.groupToolbarSpaceTitleView.text = spaceSummary.displayName } 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 8aa58632b3..1d57b26015 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 @@ -35,6 +35,7 @@ import kotlinx.coroutines.launch import org.matrix.android.sdk.api.query.ActiveSpaceFilter import org.matrix.android.sdk.api.query.RoomCategoryFilter 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.util.toMatrixItem @@ -163,7 +164,8 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho session.getPagedRoomSummariesLive( roomSummaryQueryParams { memberships = Membership.activeMemberships() - } + }, + sortOrder = RoomSortOrder.NONE ).asObservable() } 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 index a05f6ebaf8..353cf5732d 100644 --- 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 @@ -314,23 +314,7 @@ class SpaceRoomListSectionBuilder( 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 - }, + roomQueryParams.process(spaceFilterStrategy, appStateHandler.safeActiveSpaceId()), pagedListConfig ).also { when (spaceFilterStrategy) { @@ -371,7 +355,9 @@ class SpaceRoomListSectionBuilder( .subscribe { sections.find { it.sectionName == name } ?.notificationCount - ?.postValue(session.getNotificationCountForRooms(roomQueryParams)) + ?.postValue(session.getNotificationCountForRooms( + roomQueryParams.process(spaceFilterStrategy, appStateHandler.safeActiveSpaceId()) + )) }.also { onDisposable.invoke(it) } @@ -395,4 +381,24 @@ class SpaceRoomListSectionBuilder( .build() .let { block(it) } } + + internal fun RoomSummaryQueryParams.process(spaceFilter: RoomListViewModel.SpaceFilterStrategy, currentSpace: String?): RoomSummaryQueryParams { + return when (spaceFilter) { + RoomListViewModel.SpaceFilterStrategy.NORMAL -> { + copy( + activeSpaceId = ActiveSpaceFilter.ActiveSpace(currentSpace) + ) + } + RoomListViewModel.SpaceFilterStrategy.NOT_IF_ALL -> { + if (currentSpace == null) { + this + } else { + copy( + activeSpaceId = ActiveSpaceFilter.ActiveSpace(currentSpace) + ) + } + } + RoomListViewModel.SpaceFilterStrategy.NONE -> this + } + } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewState.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewState.kt index cd578bce11..558b631c16 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewState.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewState.kt @@ -20,10 +20,12 @@ import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized import org.matrix.android.sdk.api.session.room.model.RoomSummary +import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount data class SpaceListViewState( val asyncSpaces: Async> = Uninitialized, val selectedSpace: RoomSummary? = null, val rootSpaces: List? = null, - val expandedStates: Map = emptyMap() + val expandedStates: Map = emptyMap(), + val homeAggregateCount : RoomAggregateNotificationCount = RoomAggregateNotificationCount(0, 0) ) : MvRxState diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceSummaryController.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceSummaryController.kt index 2a20889545..14be8cd408 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceSummaryController.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceSummaryController.kt @@ -25,8 +25,10 @@ import im.vector.app.core.ui.list.genericItemHeader import im.vector.app.core.utils.DebouncedClickListener import im.vector.app.features.grouplist.homeSpaceSummaryItem import im.vector.app.features.home.AvatarRenderer +import im.vector.app.features.home.room.list.UnreadCounterBadgeView 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.summary.RoomAggregateNotificationCount import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject @@ -53,13 +55,15 @@ class SpaceSummaryController @Inject constructor( nonNullViewState.asyncSpaces(), nonNullViewState.selectedSpace, nonNullViewState.rootSpaces, - nonNullViewState.expandedStates) + nonNullViewState.expandedStates, + nonNullViewState.homeAggregateCount) } private fun buildGroupModels(summaries: List?, selected: RoomSummary?, rootSpaces: List?, - expandedStates: Map) { + expandedStates: Map, + homeCount: RoomAggregateNotificationCount) { if (summaries.isNullOrEmpty()) { return } @@ -99,22 +103,14 @@ class SpaceSummaryController @Inject constructor( homeSpaceSummaryItem { id(it.roomId) selected(it.roomId == selected?.roomId) + countState(UnreadCounterBadgeView.State(homeCount.totalCount, homeCount.isHighlight)) listener { callback?.onSpaceSelected(it) } } } -// summaries -// .filter { it.membership == Membership.JOIN } rootSpaces ?.forEach { groupSummary -> val isSelected = groupSummary.roomId == selected?.roomId -// if (groupSummary.roomId == ALL_COMMUNITIES_GROUP_ID) { -// homeSpaceSummaryItem { -// id(groupSummary.roomId) -// selected(isSelected) -// listener { callback?.onSpaceSelected(groupSummary) } -// } -// } else { // does it have children? val subSpaces = groupSummary.children?.filter { childInfo -> summaries.indexOfFirst { it.roomId == childInfo.childRoomId } != -1 @@ -132,24 +128,36 @@ class SpaceSummaryController @Inject constructor( onMore { callback?.onSpaceSettings(groupSummary) } listener { callback?.onSpaceSelected(groupSummary) } toggleExpand { callback?.onToggleExpand(groupSummary) } + countState( + UnreadCounterBadgeView.State( + groupSummary.notificationCount, + groupSummary.highlightCount > 0 + ) + ) } if (hasChildren && expanded) { // it's expanded - subSpaces?.forEach { child -> - summaries.firstOrNull { it.roomId == child.childRoomId }?.let { childSum -> - val isChildSelected = childSum.roomId == selected?.roomId - spaceSummaryItem { - avatarRenderer(avatarRenderer) - id(child.childRoomId) - hasChildren(false) - selected(isChildSelected) - matrixItem(MatrixItem.RoomItem(child.childRoomId, child.name, child.avatarUrl)) - listener { callback?.onSpaceSelected(childSum) } - indent(1) - } + subSpaces?.forEach { child -> + summaries.firstOrNull { it.roomId == child.childRoomId }?.let { childSum -> + val isChildSelected = childSum.roomId == selected?.roomId + spaceSummaryItem { + avatarRenderer(avatarRenderer) + id(child.childRoomId) + hasChildren(false) + selected(isChildSelected) + matrixItem(MatrixItem.RoomItem(child.childRoomId, child.name, child.avatarUrl)) + listener { callback?.onSpaceSelected(childSum) } + indent(1) + countState( + UnreadCounterBadgeView.State( + groupSummary.notificationCount, + groupSummary.highlightCount > 0 + ) + ) } } + } } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceSummaryItem.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceSummaryItem.kt index e3deca5367..797087e78b 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceSummaryItem.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceSummaryItem.kt @@ -30,6 +30,7 @@ import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.platform.CheckableConstraintLayout import im.vector.app.core.utils.DebouncedClickListener import im.vector.app.features.home.AvatarRenderer +import im.vector.app.features.home.room.list.UnreadCounterBadgeView import org.matrix.android.sdk.api.util.MatrixItem @EpoxyModelClass(layout = R.layout.item_space) @@ -44,6 +45,7 @@ abstract class SpaceSummaryItem : VectorEpoxyModel() { @EpoxyAttribute var expanded: Boolean = false @EpoxyAttribute var hasChildren: Boolean = false @EpoxyAttribute var indent: Int = 0 + @EpoxyAttribute var countState : UnreadCounterBadgeView.State = UnreadCounterBadgeView.State(0, false) override fun bind(holder: Holder) { super.bind(holder) @@ -91,6 +93,7 @@ abstract class SpaceSummaryItem : VectorEpoxyModel() { holder.indentSpace.isVisible = indent > 0 avatarRenderer.renderSpace(matrixItem, holder.avatarImageView) + holder.counterBadgeView.render(countState) } override fun unbind(holder: Holder) { @@ -105,5 +108,6 @@ abstract class SpaceSummaryItem : VectorEpoxyModel() { val moreView by bind(R.id.groupTmpLeave) val collapseIndicator by bind(R.id.groupChildrenCollapse) val indentSpace by bind(R.id.indent) + val counterBadgeView by bind(R.id.groupCounterBadge) } } 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 8889a76f9b..690e7970f1 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 @@ -32,12 +32,16 @@ import io.reactivex.Observable import io.reactivex.functions.BiFunction import kotlinx.coroutines.launch 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.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.model.RoomSummary import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams +import org.matrix.android.sdk.rx.asObservable import org.matrix.android.sdk.rx.rx +import java.util.concurrent.TimeUnit const val ALL_COMMUNITIES_GROUP_ID = "+ALL_COMMUNITIES_GROUP_ID" @@ -78,6 +82,27 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp } } .disposeOnClear() + + 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) + } + ) + setState { + copy( + homeAggregateCount = counts + ) + } + }.disposeOnClear() } private fun observeSelectionState() { diff --git a/vector/src/main/res/drawable/ic_space_icons.xml b/vector/src/main/res/drawable/ic_space_icons.xml new file mode 100644 index 0000000000..44c75908c7 --- /dev/null +++ b/vector/src/main/res/drawable/ic_space_icons.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/vector/src/main/res/layout/fragment_home_detail.xml b/vector/src/main/res/layout/fragment_home_detail.xml index bea64bcab1..419ba5b64f 100644 --- a/vector/src/main/res/layout/fragment_home_detail.xml +++ b/vector/src/main/res/layout/fragment_home_detail.xml @@ -19,15 +19,50 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_vertical" - android:orientation="horizontal"> + android:orientation="horizontal" + android:baselineAligned="false"> - + android:layout_marginTop="8dp" + android:layout_marginBottom="8dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + android:layout_width="40dp" + android:layout_height="40dp"> + + + + + + + + + + + + + + + + + + + + + + + + + +