Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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 @@ -44,6 +44,13 @@ class UnreadCounterBadgeView : MaterialTextView {
}
}

fun renderWithText(text: String, highlighted: Boolean) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would prefer to have one render method with sealed class State instead

visibility = View.VISIBLE
val bgRes = if (highlighted) R.drawable.bg_unread_highlight else R.drawable.bg_unread_notification
setBackgroundResource(bgRes)
this.text = text
}

data class State(
val count: Int,
val highlighted: Boolean
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 @@ -51,6 +52,7 @@ class NewSpaceSummaryController @Inject constructor(
nonNullViewState.spaces,
nonNullViewState.selectedSpace,
nonNullViewState.rootSpacesOrdered,
nonNullViewState.inviters,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pass the nonNullViewState instead of each values?

nonNullViewState.homeAggregateCount,
nonNullViewState.expandedStates,
)
Expand All @@ -60,6 +62,7 @@ class NewSpaceSummaryController @Inject constructor(
spaceSummaries: List<RoomSummary>?,
selectedSpace: RoomSummary?,
rootSpaces: List<RoomSummary>?,
inviters: List<User>,
homeCount: RoomAggregateNotificationCount,
expandedStates: Map<String, Boolean>,
) {
Expand All @@ -69,6 +72,7 @@ class NewSpaceSummaryController @Inject constructor(

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

Expand All @@ -91,7 +95,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 Down Expand Up @@ -159,6 +163,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
@@ -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.renderWithText("!", 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
80 changes: 80 additions & 0 deletions vector/src/main/res/layout/item_space_invite.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<im.vector.app.core.platform.CheckableConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="65dp"
android:background="@drawable/bg_space_item"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
tools:viewBindingIgnore="true">

<ImageView
android:id="@+id/avatar"
android:layout_width="42dp"
android:layout_height="42dp"
android:layout_gravity="center"
android:layout_marginStart="@dimen/layout_horizontal_margin"
android:duplicateParentState="true"
android:importantForAccessibility="no"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@sample/space_avatars" />

<TextView
android:id="@+id/name"
style="@style/Widget.Vector.TextView.Subtitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/layout_horizontal_margin"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?vctr_content_primary"
android:textStyle="bold"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintBottom_toTopOf="@id/invited_by"
app:layout_constraintEnd_toStartOf="@id/unread_counter"
app:layout_constraintStart_toEndOf="@id/avatar"
app:layout_constraintTop_toTopOf="parent"
tools:text="Element Corp" />

<TextView
android:id="@+id/invited_by"
style="@style/Widget.Vector.TextView.Body"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/layout_horizontal_margin"
android:ellipsize="end"
android:layout_marginTop="2dp"
android:maxLines="1"
android:textColor="?vctr_content_secondary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/unread_counter"
app:layout_constraintStart_toEndOf="@id/avatar"
app:layout_constraintTop_toBottomOf="@id/name"
tools:text="Invited by John Smith" />

<im.vector.app.features.home.room.list.UnreadCounterBadgeView
android:id="@+id/notification_badge"
style="@style/Widget.Vector.TextView.Micro"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginEnd="52dp"
android:gravity="center"
android:minWidth="16dp"
android:minHeight="16dp"
android:paddingStart="4dp"
android:paddingEnd="4dp"
android:textColor="?colorOnError"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:background="@drawable/bg_unread_highlight"
tools:text="!"
tools:visibility="visible" />

</im.vector.app.core.platform.CheckableConstraintLayout>