From 24a32f2b7e33d6f22c50f9735de9f8a9a4839454 Mon Sep 17 00:00:00 2001 From: Valere Date: Tue, 11 May 2021 10:07:34 +0200 Subject: [PATCH] Subspace more hierarchy depth + fix bad notification count --- .../grouplist/HomeSpaceSummaryItem.kt | 6 +- .../features/spaces/SpaceSummaryController.kt | 60 +++++--- .../app/features/spaces/SpaceSummaryItem.kt | 12 +- .../features/spaces/SubSpaceSummaryItem.kt | 106 ++++++++++++++ vector/src/main/res/layout/item_sub_space.xml | 132 ++++++++++++++++++ 5 files changed, 292 insertions(+), 24 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/spaces/SubSpaceSummaryItem.kt create mode 100644 vector/src/main/res/layout/item_sub_space.xml 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 553f82e98f..ddda38aa9c 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 @@ -18,6 +18,7 @@ package im.vector.app.features.grouplist import android.content.res.ColorStateList +import android.view.View import android.widget.ImageView import android.widget.TextView import androidx.core.content.ContextCompat @@ -35,8 +36,9 @@ import im.vector.app.features.themes.ThemeUtils abstract class HomeSpaceSummaryItem : VectorEpoxyModel() { @EpoxyAttribute var selected: Boolean = false - @EpoxyAttribute var listener: (() -> Unit)? = null + @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var listener: (() -> Unit)? = null @EpoxyAttribute var countState : UnreadCounterBadgeView.State = UnreadCounterBadgeView.State(0, false) + @EpoxyAttribute var showSeparator: Boolean = false override fun getViewType(): Int { // mm.. it's reusing the same layout for basic space item @@ -56,6 +58,7 @@ abstract class HomeSpaceSummaryItem : VectorEpoxyModel(R.id.itemGroupLayout) val leaveView by bind(R.id.groupTmpLeave) val counterBadgeView by bind(R.id.groupCounterBadge) + val bottomSeparator by bind(R.id.groupBottomSeparator) } } 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 4b78a999bb..6bd56c9d81 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 @@ -31,8 +31,8 @@ import im.vector.app.space import org.matrix.android.sdk.api.session.group.model.GroupSummary 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.SpaceChildInfo 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 @@ -163,24 +163,7 @@ class SpaceSummaryController @Inject constructor( if (hasChildren && expanded) { // it's expanded subSpaces?.forEach { child -> - summaries?.firstOrNull { it.roomId == child.childRoomId }?.let { childSum -> - val isChildSelected = selected is RoomGroupingMethod.BySpace && childSum.roomId == selected.space()?.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 - ) - ) - } - } + buildSubSpace(summaries, expandedStates, selected, child, 1, 3) } } } @@ -191,6 +174,45 @@ class SpaceSummaryController @Inject constructor( } } + private fun buildSubSpace(summaries: List?, + expandedStates: Map, + selected: RoomGroupingMethod, + childSum: SpaceChildInfo, currentDepth: Int, maxDepth: Int) { + if (currentDepth >= maxDepth) return + val childSum = summaries?.firstOrNull { it.roomId == childSum.childRoomId } ?: return + // does it have children? + val subSpaces = childSum.spaceChildren?.filter { childInfo -> + summaries.indexOfFirst { it.roomId == childInfo.childRoomId } != -1 + } + val expanded = expandedStates[childSum.roomId] == true + val isSelected = selected is RoomGroupingMethod.BySpace && childSum.roomId == selected.space()?.roomId + + subSpaceSummaryItem { + avatarRenderer(avatarRenderer) + id(childSum.roomId) + hasChildren(!subSpaces.isNullOrEmpty()) + selected(isSelected) + expanded(expanded) + onMore { callback?.onSpaceSettings(childSum) } + matrixItem(childSum.toMatrixItem()) + listener { callback?.onSpaceSelected(childSum) } + toggleExpand { callback?.onToggleExpand(childSum) } + indent(currentDepth) + countState( + UnreadCounterBadgeView.State( + childSum.notificationCount, + childSum.highlightCount > 0 + ) + ) + } + + if (expanded) { + subSpaces?.forEach { + buildSubSpace(summaries, expandedStates, selected, it, currentDepth + 1, maxDepth) + } + } + } + interface Callback { fun onSpaceSelected(spaceSummary: RoomSummary?) fun onSpaceInviteSelected(spaceSummary: RoomSummary) 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 013f7d6db5..c029f4b25e 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 @@ -16,6 +16,7 @@ package im.vector.app.features.spaces +import android.view.View import android.widget.ImageView import android.widget.Space import android.widget.TextView @@ -37,17 +38,18 @@ import org.matrix.android.sdk.api.util.MatrixItem @EpoxyModelClass(layout = R.layout.item_space) abstract class SpaceSummaryItem : VectorEpoxyModel() { - @EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer + @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) lateinit var avatarRenderer: AvatarRenderer @EpoxyAttribute lateinit var matrixItem: MatrixItem @EpoxyAttribute var selected: Boolean = false - @EpoxyAttribute var listener: (() -> Unit)? = null - @EpoxyAttribute var onMore: (() -> Unit)? = null - @EpoxyAttribute var toggleExpand: (() -> Unit)? = null + @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var listener: (() -> Unit)? = null + @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var onMore: (() -> Unit)? = null + @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var toggleExpand: (() -> Unit)? = null @EpoxyAttribute var expanded: Boolean = false @EpoxyAttribute var hasChildren: Boolean = false @EpoxyAttribute var indent: Int = 0 @EpoxyAttribute var countState : UnreadCounterBadgeView.State = UnreadCounterBadgeView.State(0, false) @EpoxyAttribute var description: String? = null + @EpoxyAttribute var showSeparator: Boolean = false override fun bind(holder: Holder) { super.bind(holder) @@ -83,6 +85,7 @@ abstract class SpaceSummaryItem : VectorEpoxyModel() { } holder.indentSpace.isVisible = indent > 0 + holder.separator.isVisible = showSeparator avatarRenderer.renderSpace(matrixItem, holder.avatarImageView) holder.counterBadgeView.render(countState) @@ -102,5 +105,6 @@ abstract class SpaceSummaryItem : VectorEpoxyModel() { val collapseIndicator by bind(R.id.groupChildrenCollapse) val indentSpace by bind(R.id.indent) val counterBadgeView by bind(R.id.groupCounterBadge) + val separator by bind(R.id.groupBottomSeparator) } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/SubSpaceSummaryItem.kt b/vector/src/main/java/im/vector/app/features/spaces/SubSpaceSummaryItem.kt new file mode 100644 index 0000000000..b98f1ca7af --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/SubSpaceSummaryItem.kt @@ -0,0 +1,106 @@ +/* + * 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.spaces + +import android.widget.ImageView +import android.widget.Space +import android.widget.TextView +import androidx.core.content.ContextCompat +import androidx.core.view.isGone +import androidx.core.view.isVisible +import androidx.core.view.updateLayoutParams +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +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.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_sub_space) +abstract class SubSpaceSummaryItem : VectorEpoxyModel() { + + @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) lateinit var avatarRenderer: AvatarRenderer + @EpoxyAttribute lateinit var matrixItem: MatrixItem + @EpoxyAttribute var selected: Boolean = false + @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var listener: (() -> Unit)? = null + @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var onMore: (() -> Unit)? = null + @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var toggleExpand: (() -> Unit)? = null + @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) + holder.rootView.setOnClickListener { listener?.invoke() } + holder.groupNameView.text = matrixItem.displayName + holder.rootView.isChecked = selected + if (onMore != null) { + holder.moreView.isVisible = true + holder.moreView.setOnClickListener( + DebouncedClickListener({ _ -> + onMore?.invoke() + }) + ) + } else { + holder.moreView.isVisible = false + } + + if (hasChildren) { + holder.collapseIndicator.isVisible = true + holder.collapseIndicator.setImageDrawable( + ContextCompat.getDrawable(holder.view.context, + if (expanded) R.drawable.ic_expand_less else R.drawable.ic_expand_more + ) + ) + holder.collapseIndicator.setOnClickListener( + DebouncedClickListener({ _ -> + toggleExpand?.invoke() + }) + ) + } else { + holder.collapseIndicator.isGone = true + } + + holder.indentSpace.isVisible = indent > 0 + holder.indentSpace.updateLayoutParams { + width = indent * 30 + } + + avatarRenderer.renderSpace(matrixItem, holder.avatarImageView) + holder.counterBadgeView.render(countState) + } + + override fun unbind(holder: Holder) { + avatarRenderer.clear(holder.avatarImageView) + super.unbind(holder) + } + + class Holder : VectorEpoxyHolder() { + val avatarImageView by bind(R.id.groupAvatarImageView) + val groupNameView by bind(R.id.groupNameView) + val rootView by bind(R.id.itemGroupLayout) + 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/res/layout/item_sub_space.xml b/vector/src/main/res/layout/item_sub_space.xml new file mode 100644 index 0000000000..e01b9a4cc7 --- /dev/null +++ b/vector/src/main/res/layout/item_sub_space.xml @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file