Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Space Switching: Space Invites #6924

Merged
merged 6 commits into from
Aug 30, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/6924.wip
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[New Layout] Adds space invites
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ abstract class HomeSpaceSummaryItem : VectorEpoxyModel<HomeSpaceSummaryItem.Hold
@EpoxyAttribute var text: String = ""
@EpoxyAttribute var selected: Boolean = false
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var listener: ClickListener? = null
@EpoxyAttribute var countState: UnreadCounterBadgeView.State = UnreadCounterBadgeView.State(0, false)
@EpoxyAttribute var countState: UnreadCounterBadgeView.State = UnreadCounterBadgeView.State.Count(0, false)
@EpoxyAttribute var showSeparator: Boolean = false

override fun getViewType(): Int {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ abstract class NewHomeSpaceSummaryItem : VectorEpoxyModel<NewHomeSpaceSummaryIte
@EpoxyAttribute var text: String = ""
@EpoxyAttribute var selected: Boolean = false
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var listener: ClickListener? = null
@EpoxyAttribute var countState: UnreadCounterBadgeView.State = UnreadCounterBadgeView.State(0, false)
@EpoxyAttribute var countState: UnreadCounterBadgeView.State = UnreadCounterBadgeView.State.Count(0, false)
@EpoxyAttribute var showSeparator: Boolean = false

override fun getViewType() = R.id.space_item_home
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ class HomeDetailFragment :

unreadMessagesSharedViewModel.onEach { state ->
views.drawerUnreadCounterBadgeView.render(
UnreadCounterBadgeView.State(
UnreadCounterBadgeView.State.Count(
count = state.otherSpacesUnread.totalCount,
highlighted = state.otherSpacesUnread.isHighlight
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ abstract class BreadcrumbsItem : VectorEpoxyModel<BreadcrumbsItem.Holder>(R.layo
holder.unreadIndentIndicator.isVisible = hasUnreadMessage
avatarRenderer.render(matrixItem, holder.avatarImageView)
holder.avatarImageView.contentDescription = matrixItem.getBestName()
holder.unreadCounterBadgeView.render(UnreadCounterBadgeView.State(unreadNotificationCount, showHighlighted))
holder.unreadCounterBadgeView.render(UnreadCounterBadgeView.State.Count(unreadNotificationCount, showHighlighted))
holder.draftIndentIndicator.isVisible = hasDraft
holder.typingIndicator.isVisible = hasTypingUsers
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ abstract class RoomCategoryItem : VectorEpoxyModel<RoomCategoryItem.Holder>(R.la
val expandedArrowDrawable = ContextCompat.getDrawable(holder.rootView.context, expandedArrowDrawableRes)?.also {
DrawableCompat.setTint(it, tintColor)
}
holder.unreadCounterBadgeView.render(UnreadCounterBadgeView.State(unreadNotificationCount, showHighlighted))
holder.unreadCounterBadgeView.render(UnreadCounterBadgeView.State.Count(unreadNotificationCount, showHighlighted))
holder.titleView.text = title
holder.counterView.text = itemCount.takeIf { it > 0 }?.toString().orEmpty()
holder.counterView.setCompoundDrawablesWithIntrinsicBounds(null, null, expandedArrowDrawable, null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>(R.layo
itemLongClickListener?.onLongClick(it) ?: false
}
holder.titleView.text = matrixItem.getBestName()
holder.unreadCounterBadgeView.render(UnreadCounterBadgeView.State(unreadNotificationCount, showHighlighted))
holder.unreadCounterBadgeView.render(UnreadCounterBadgeView.State.Count(unreadNotificationCount, showHighlighted))
holder.unreadIndentIndicator.isVisible = hasUnreadMessage
holder.draftView.isVisible = hasDraft
avatarRenderer.render(matrixItem, holder.avatarImageView)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ class SectionHeaderAdapter constructor(
binding.root.isClickable = roomsSectionData.isCollapsable
binding.roomCategoryCounterView.setCompoundDrawablesWithIntrinsicBounds(null, null, collapsableArrowDrawable, null)
binding.roomCategoryCounterView.text = roomsSectionData.itemCount.takeIf { it > 0 }?.toString().orEmpty()
binding.roomCategoryUnreadCounterBadgeView.render(UnreadCounterBadgeView.State(roomsSectionData.notificationCount, roomsSectionData.isHighlighted))
binding.roomCategoryUnreadCounterBadgeView.render(UnreadCounterBadgeView.State.Count(roomsSectionData.notificationCount, roomsSectionData.isHighlighted))
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,44 @@ class UnreadCounterBadgeView : MaterialTextView {
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)

fun render(state: State) {
if (state.count == 0) {
visibility = View.INVISIBLE
} else {
visibility = View.VISIBLE
val bgRes = if (state.highlighted) {
R.drawable.bg_unread_highlight
when(state) {
is State.Count -> renderAsCount(state)
is State.Text -> renderAsText(state)
}
}

private fun renderAsCount(state: State.Count) {
val view = this

with(state) {
if (count == 0) {
visibility = View.INVISIBLE
} else {
R.drawable.bg_unread_notification
visibility = View.VISIBLE
val bgRes = if (highlighted) {
R.drawable.bg_unread_highlight
} else {
R.drawable.bg_unread_notification
}
setBackgroundResource(bgRes)
view.text = RoomSummaryFormatter.formatUnreadMessagesCounter(count)
}
}
}

private fun renderAsText(state: State.Text) {
val view = this

with(state) {
visibility = View.VISIBLE
val bgRes = if (highlighted) R.drawable.bg_unread_highlight else R.drawable.bg_unread_notification
setBackgroundResource(bgRes)
text = RoomSummaryFormatter.formatUnreadMessagesCounter(state.count)
view.text = text
}
}

data class State(
val count: Int,
val highlighted: Boolean
)
sealed class State {
data class Count(val count: Int, val highlighted: Boolean) : State()
data class Text(val text: String, val highlighted: Boolean) : State()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ abstract class InviteCounterItem : VectorEpoxyModel<InviteCounterItem.Holder>(R.
override fun bind(holder: Holder) {
super.bind(holder)
holder.view.setOnClickListener(listener)
holder.unreadCounterBadgeView.render(UnreadCounterBadgeView.State(invitesCount, true))
holder.unreadCounterBadgeView.render(UnreadCounterBadgeView.State.Count(invitesCount, true))
}

class Holder : VectorEpoxyHolder() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ abstract class RecentRoomItem : VectorEpoxyModel<RecentRoomItem.Holder>(R.layout

avatarRenderer.render(matrixItem, holder.avatarImageView)
holder.avatarImageView.contentDescription = matrixItem.getBestName()
holder.unreadCounterBadgeView.render(UnreadCounterBadgeView.State(unreadNotificationCount, showHighlighted))
holder.unreadCounterBadgeView.render(UnreadCounterBadgeView.State.Count(unreadNotificationCount, showHighlighted))
holder.title.text = matrixItem.getBestName()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ 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.session.user.model.User
import org.matrix.android.sdk.api.util.toMatrixItem
import javax.inject.Inject

Expand All @@ -47,28 +48,17 @@ class NewSpaceSummaryController @Inject constructor(

override fun buildModels() {
val nonNullViewState = viewState ?: return
buildGroupModels(
nonNullViewState.spaces,
nonNullViewState.selectedSpace,
nonNullViewState.rootSpacesOrdered,
nonNullViewState.homeAggregateCount,
nonNullViewState.expandedStates,
)
buildGroupModels(nonNullViewState)
}

private fun buildGroupModels(
spaceSummaries: List<RoomSummary>?,
selectedSpace: RoomSummary?,
rootSpaces: List<RoomSummary>?,
homeCount: RoomAggregateNotificationCount,
expandedStates: Map<String, Boolean>,
) {
private fun buildGroupModels(viewState: SpaceListViewState) = with(viewState) {
newSpaceListHeaderItem {
id("space_list_header")
}

addHomeItem(selectedSpace == null, homeCount)
addSpaces(spaceSummaries, selectedSpace, rootSpaces, expandedStates)
addHomeItem(selectedSpace == null, homeAggregateCount)
addSpaces(spaces, selectedSpace, rootSpacesOrdered, expandedStates)
addInvites(selectedSpace, rootSpacesOrdered, inviters)
addCreateItem()
}

Expand All @@ -78,7 +68,7 @@ class NewSpaceSummaryController @Inject constructor(
id("space_home")
text(host.stringProvider.getString(R.string.all_chats))
selected(selected)
countState(UnreadCounterBadgeView.State(homeCount.totalCount, homeCount.isHighlight))
countState(UnreadCounterBadgeView.State.Count(homeCount.totalCount, homeCount.isHighlight))
listener { host.callback?.onSpaceSelected(null) }
}
}
Expand All @@ -91,7 +81,7 @@ class NewSpaceSummaryController @Inject constructor(
) {
val host = this

rootSpaces?.filter { it.membership != Membership.INVITE }
rootSpaces?.filterNot { it.membership == Membership.INVITE }
?.forEach { spaceSummary ->
val subSpaces = spaceSummary.spaceChildren?.filter { spaceChild -> spaceSummaries.containsSpaceId(spaceChild.childRoomId) }
val hasChildren = (subSpaces?.size ?: 0) > 0
Expand All @@ -101,7 +91,7 @@ class NewSpaceSummaryController @Inject constructor(
newSpaceSummaryItem {
id(spaceSummary.roomId)
avatarRenderer(host.avatarRenderer)
countState(UnreadCounterBadgeView.State(spaceSummary.notificationCount, spaceSummary.highlightCount > 0))
countState(UnreadCounterBadgeView.State.Count(spaceSummary.notificationCount, spaceSummary.highlightCount > 0))
expanded(expanded)
hasChildren(hasChildren)
matrixItem(spaceSummary.toMatrixItem())
Expand Down Expand Up @@ -132,7 +122,7 @@ class NewSpaceSummaryController @Inject constructor(
val host = this
val childSummary = spaceSummaries?.firstOrNull { it.roomId == info.childRoomId } ?: return
val id = "$idPrefix:${childSummary.roomId}"
val countState = UnreadCounterBadgeView.State(childSummary.notificationCount, childSummary.highlightCount > 0)
val countState = UnreadCounterBadgeView.State.Count(childSummary.notificationCount, childSummary.highlightCount > 0)
val expanded = expandedStates[childSummary.roomId] == true
val isSelected = childSummary.roomId == selectedSpace?.roomId
val subSpaces = childSummary.spaceChildren?.filter { childSpace -> spaceSummaries.containsSpaceId(childSpace.childRoomId) }
Expand All @@ -159,6 +149,30 @@ class NewSpaceSummaryController @Inject constructor(
}
}

private fun addInvites(
selectedSpace: RoomSummary?,
rootSpaces: List<RoomSummary>?,
inviters: List<User>,
) {
val host = this

rootSpaces?.filter { it.membership == Membership.INVITE }
?.forEach { spaceSummary ->
val isSelected = spaceSummary.roomId == selectedSpace?.roomId
val inviter = inviters.find { it.userId == spaceSummary.inviterId }

spaceInviteItem {
id(spaceSummary.roomId)
avatarRenderer(host.avatarRenderer)
inviter(inviter?.displayName.orEmpty())
matrixItem(spaceSummary.toMatrixItem())
onLongClickListener { host.callback?.onSpaceSettings(spaceSummary) }
onInviteSelectedListener { host.callback?.onSpaceInviteSelected(spaceSummary) }
selected(isSelected)
}
}
}

private fun addCreateItem() {
val host = this
newSpaceAddItem {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import org.matrix.android.sdk.api.util.MatrixItem
abstract class NewSpaceSummaryItem : VectorEpoxyModel<NewSpaceSummaryItem.Holder>(R.layout.item_new_space) {

@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) lateinit var avatarRenderer: AvatarRenderer
@EpoxyAttribute var countState: UnreadCounterBadgeView.State = UnreadCounterBadgeView.State(0, false)
@EpoxyAttribute var countState: UnreadCounterBadgeView.State = UnreadCounterBadgeView.State.Count(0, false)
@EpoxyAttribute var expanded: Boolean = false
@EpoxyAttribute var hasChildren: Boolean = false
@EpoxyAttribute lateinit var matrixItem: MatrixItem
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import org.matrix.android.sdk.api.util.MatrixItem
abstract class NewSubSpaceSummaryItem : VectorEpoxyModel<NewSubSpaceSummaryItem.Holder>(R.layout.item_new_sub_space) {

@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) lateinit var avatarRenderer: AvatarRenderer
@EpoxyAttribute var countState: UnreadCounterBadgeView.State = UnreadCounterBadgeView.State(0, false)
@EpoxyAttribute var countState: UnreadCounterBadgeView.State = UnreadCounterBadgeView.State.Count(0, false)
@EpoxyAttribute var expanded: Boolean = false
@EpoxyAttribute var hasChildren: Boolean = false
@EpoxyAttribute var indent: Int = 0
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* 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.TextView
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.epoxy.onClick
import im.vector.app.core.platform.CheckableConstraintLayout
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
abstract class SpaceInviteItem : VectorEpoxyModel<SpaceInviteItem.Holder>(R.layout.item_space_invite) {

@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) lateinit var avatarRenderer: AvatarRenderer
@EpoxyAttribute var inviter: String = ""
@EpoxyAttribute lateinit var matrixItem: MatrixItem
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var onLongClickListener: ClickListener? = null
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var onInviteSelectedListener: ClickListener? = null
@EpoxyAttribute var selected: Boolean = false

override fun bind(holder: Holder) {
super.bind(holder)
val context = holder.root.context
holder.root.isChecked = selected
holder.root.onClick(onInviteSelectedListener)
holder.root.setOnLongClickListener { onLongClickListener?.invoke(holder.root).let { true } }
holder.name.text = matrixItem.displayName
holder.invitedBy.text = context.getString(R.string.invited_by, inviter)

avatarRenderer.render(matrixItem, holder.avatar)
holder.notificationBadge.render(UnreadCounterBadgeView.State.Text("!", true))
}

override fun unbind(holder: Holder) {
avatarRenderer.clear(holder.avatar)
super.unbind(holder)
}

class Holder : VectorEpoxyHolder() {
val root by bind<CheckableConstraintLayout>(R.id.root)
val avatar by bind<ImageView>(R.id.avatar)
val name by bind<TextView>(R.id.name)
val invitedBy by bind<TextView>(R.id.invited_by)
val notificationBadge by bind<UnreadCounterBadgeView>(R.id.notification_badge)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -273,11 +273,14 @@ class SpaceListViewModel @AssistedInject constructor(
?.content.toModel<SpaceOrderContent>()
?.safeOrder()
}
val inviterIds = spaces.mapNotNull { it.inviterId }
val inviters = inviterIds.mapNotNull { session.userService().getUser(it) }
copy(
asyncSpaces = asyncSpaces,
spaces = spaces,
inviters = inviters,
rootSpacesOrdered = rootSpaces.sortedWith(TopLevelSpaceComparator(orders)),
spaceOrderInfo = orders
spaceOrderInfo = orders,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@ import com.airbnb.mvrx.MavericksState
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
import org.matrix.android.sdk.api.session.user.model.User
import org.matrix.android.sdk.api.util.MatrixItem

data class SpaceListViewState(
val myMxItem: Async<MatrixItem.UserItem> = Uninitialized,
val asyncSpaces: Async<List<RoomSummary>> = Uninitialized,
val spaces: List<RoomSummary> = emptyList(),
val selectedSpace: RoomSummary? = null,
val inviters: List<User> = emptyList(),
val rootSpacesOrdered: List<RoomSummary>? = null,
val spaceOrderInfo: Map<String, String?>? = null,
val spaceOrderLocalEchos: Map<String, String?>? = null,
Expand Down
Loading