forked from GitHub-Mirror/riotX-android
Room list: rework invitations
This commit is contained in:
parent
b25098c52d
commit
07309c90e1
@ -29,7 +29,7 @@ import im.vector.riotredesign.features.themes.ThemeUtils
|
|||||||
/**
|
/**
|
||||||
* Set a text in the TextView, or set visibility to GONE if the text is null
|
* Set a text in the TextView, or set visibility to GONE if the text is null
|
||||||
*/
|
*/
|
||||||
fun TextView.setTextOrHide(newText: String?, hideWhenBlank: Boolean = true) {
|
fun TextView.setTextOrHide(newText: CharSequence?, hideWhenBlank: Boolean = true) {
|
||||||
if (newText == null
|
if (newText == null
|
||||||
|| (newText.isBlank() && hideWhenBlank)) {
|
|| (newText.isBlank() && hideWhenBlank)) {
|
||||||
isVisible = false
|
isVisible = false
|
||||||
|
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 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.riotredesign.features.home.room.list
|
||||||
|
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.Button
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import com.airbnb.epoxy.EpoxyAttribute
|
||||||
|
import com.airbnb.epoxy.EpoxyModelClass
|
||||||
|
import im.vector.riotredesign.R
|
||||||
|
import im.vector.riotredesign.core.epoxy.VectorEpoxyHolder
|
||||||
|
import im.vector.riotredesign.core.epoxy.VectorEpoxyModel
|
||||||
|
import im.vector.riotredesign.core.extensions.setTextOrHide
|
||||||
|
import im.vector.riotredesign.features.home.AvatarRenderer
|
||||||
|
|
||||||
|
|
||||||
|
@EpoxyModelClass(layout = R.layout.item_room_invitation)
|
||||||
|
abstract class RoomInvitationItem : VectorEpoxyModel<RoomInvitationItem.Holder>() {
|
||||||
|
|
||||||
|
@EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
|
||||||
|
@EpoxyAttribute lateinit var roomName: CharSequence
|
||||||
|
@EpoxyAttribute lateinit var roomId: String
|
||||||
|
@EpoxyAttribute var secondLine: CharSequence? = null
|
||||||
|
@EpoxyAttribute var avatarUrl: String? = null
|
||||||
|
@EpoxyAttribute var listener: (() -> Unit)? = null
|
||||||
|
@EpoxyAttribute var acceptListener: (() -> Unit)? = null
|
||||||
|
@EpoxyAttribute var rejectListener: (() -> Unit)? = null
|
||||||
|
|
||||||
|
|
||||||
|
override fun bind(holder: Holder) {
|
||||||
|
super.bind(holder)
|
||||||
|
holder.rootView.setOnClickListener { listener?.invoke() }
|
||||||
|
holder.acceptView.setOnClickListener { acceptListener?.invoke() }
|
||||||
|
holder.rejectView.setOnClickListener { rejectListener?.invoke() }
|
||||||
|
holder.titleView.text = roomName
|
||||||
|
holder.subtitleView.setTextOrHide(secondLine)
|
||||||
|
avatarRenderer.render(avatarUrl, roomId, roomName.toString(), holder.avatarImageView)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Holder : VectorEpoxyHolder() {
|
||||||
|
val titleView by bind<TextView>(R.id.roomInvitationNameView)
|
||||||
|
val subtitleView by bind<TextView>(R.id.roomInvitationSubTitle)
|
||||||
|
val acceptView by bind<Button>(R.id.roomInvitationAccept)
|
||||||
|
val rejectView by bind<Button>(R.id.roomInvitationReject)
|
||||||
|
val avatarImageView by bind<ImageView>(R.id.roomInvitationAvatarImageView)
|
||||||
|
val rootView by bind<ViewGroup>(R.id.itemRoomInvitationLayout)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -45,7 +45,7 @@ data class RoomListParams(
|
|||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
|
|
||||||
class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Callback, OnBackPressed, FabMenuView.Listener {
|
class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Listener, OnBackPressed, FabMenuView.Listener {
|
||||||
|
|
||||||
enum class DisplayMode(@StringRes val titleRes: Int) {
|
enum class DisplayMode(@StringRes val titleRes: Int) {
|
||||||
HOME(R.string.bottom_action_home),
|
HOME(R.string.bottom_action_home),
|
||||||
@ -135,7 +135,7 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Callback, O
|
|||||||
val stateRestorer = LayoutManagerStateRestorer(layoutManager).register()
|
val stateRestorer = LayoutManagerStateRestorer(layoutManager).register()
|
||||||
epoxyRecyclerView.layoutManager = layoutManager
|
epoxyRecyclerView.layoutManager = layoutManager
|
||||||
epoxyRecyclerView.itemAnimator = RoomListAnimator()
|
epoxyRecyclerView.itemAnimator = RoomListAnimator()
|
||||||
roomController.callback = this
|
roomController.listener = this
|
||||||
roomController.addModelBuildListener { it.dispatchTo(stateRestorer) }
|
roomController.addModelBuildListener { it.dispatchTo(stateRestorer) }
|
||||||
stateView.contentView = epoxyRecyclerView
|
stateView.contentView = epoxyRecyclerView
|
||||||
epoxyRecyclerView.setController(roomController)
|
epoxyRecyclerView.setController(roomController)
|
||||||
@ -233,6 +233,14 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Callback, O
|
|||||||
roomListViewModel.accept(RoomListActions.SelectRoom(room))
|
roomListViewModel.accept(RoomListActions.SelectRoom(room))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onAcceptRoomInvitation(room: RoomSummary) {
|
||||||
|
vectorBaseActivity.notImplemented("Accept room invitation")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRejectRoomInvitation(room: RoomSummary) {
|
||||||
|
vectorBaseActivity.notImplemented("Reject room invitation")
|
||||||
|
}
|
||||||
|
|
||||||
override fun onToggleRoomCategory(roomCategory: RoomCategory) {
|
override fun onToggleRoomCategory(roomCategory: RoomCategory) {
|
||||||
roomListViewModel.accept(RoomListActions.ToggleCategory(roomCategory))
|
roomListViewModel.accept(RoomListActions.ToggleCategory(roomCategory))
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri
|
|||||||
private val roomSummaryItemFactory: RoomSummaryItemFactory
|
private val roomSummaryItemFactory: RoomSummaryItemFactory
|
||||||
) : TypedEpoxyController<RoomListViewState>() {
|
) : TypedEpoxyController<RoomListViewState>() {
|
||||||
|
|
||||||
var callback: Callback? = null
|
var listener: Listener? = null
|
||||||
|
|
||||||
override fun buildModels(viewState: RoomListViewState) {
|
override fun buildModels(viewState: RoomListViewState) {
|
||||||
val roomSummaries = viewState.asyncFilteredRooms()
|
val roomSummaries = viewState.asyncFilteredRooms()
|
||||||
@ -36,7 +36,7 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri
|
|||||||
} else {
|
} else {
|
||||||
val isExpanded = viewState.isCategoryExpanded(category)
|
val isExpanded = viewState.isCategoryExpanded(category)
|
||||||
buildRoomCategory(viewState, summaries, category.titleRes, viewState.isCategoryExpanded(category)) {
|
buildRoomCategory(viewState, summaries, category.titleRes, viewState.isCategoryExpanded(category)) {
|
||||||
callback?.onToggleRoomCategory(category)
|
listener?.onToggleRoomCategory(category)
|
||||||
}
|
}
|
||||||
if (isExpanded) {
|
if (isExpanded) {
|
||||||
buildRoomModels(summaries)
|
buildRoomModels(summaries)
|
||||||
@ -76,14 +76,16 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri
|
|||||||
private fun buildRoomModels(summaries: List<RoomSummary>) {
|
private fun buildRoomModels(summaries: List<RoomSummary>) {
|
||||||
summaries.forEach { roomSummary ->
|
summaries.forEach { roomSummary ->
|
||||||
roomSummaryItemFactory
|
roomSummaryItemFactory
|
||||||
.create(roomSummary) { callback?.onRoomSelected(it) }
|
.create(roomSummary, listener)
|
||||||
.addTo(this)
|
.addTo(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Callback {
|
interface Listener {
|
||||||
fun onToggleRoomCategory(roomCategory: RoomCategory)
|
fun onToggleRoomCategory(roomCategory: RoomCategory)
|
||||||
fun onRoomSelected(room: RoomSummary)
|
fun onRoomSelected(room: RoomSummary)
|
||||||
|
fun onRejectRoomInvitation(room: RoomSummary)
|
||||||
|
fun onAcceptRoomInvitation(room: RoomSummary)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,12 +18,15 @@ package im.vector.riotredesign.features.home.room.list
|
|||||||
|
|
||||||
import im.vector.matrix.android.api.session.events.model.EventType
|
import im.vector.matrix.android.api.session.events.model.EventType
|
||||||
import im.vector.matrix.android.api.session.events.model.toModel
|
import im.vector.matrix.android.api.session.events.model.toModel
|
||||||
|
import im.vector.matrix.android.api.session.room.model.Membership
|
||||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||||
import im.vector.matrix.android.api.session.room.model.message.MessageContent
|
import im.vector.matrix.android.api.session.room.model.message.MessageContent
|
||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
||||||
|
import im.vector.riotredesign.core.epoxy.VectorEpoxyModel
|
||||||
import im.vector.riotredesign.core.extensions.localDateTime
|
import im.vector.riotredesign.core.extensions.localDateTime
|
||||||
import im.vector.riotredesign.core.resources.ColorProvider
|
import im.vector.riotredesign.core.resources.ColorProvider
|
||||||
import im.vector.riotredesign.core.resources.DateProvider
|
import im.vector.riotredesign.core.resources.DateProvider
|
||||||
|
import im.vector.riotredesign.core.resources.StringProvider
|
||||||
import im.vector.riotredesign.features.home.AvatarRenderer
|
import im.vector.riotredesign.features.home.AvatarRenderer
|
||||||
import im.vector.riotredesign.features.home.room.detail.timeline.format.NoticeEventFormatter
|
import im.vector.riotredesign.features.home.room.detail.timeline.format.NoticeEventFormatter
|
||||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter
|
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter
|
||||||
@ -34,9 +37,39 @@ import javax.inject.Inject
|
|||||||
class RoomSummaryItemFactory @Inject constructor(private val noticeEventFormatter: NoticeEventFormatter,
|
class RoomSummaryItemFactory @Inject constructor(private val noticeEventFormatter: NoticeEventFormatter,
|
||||||
private val timelineDateFormatter: TimelineDateFormatter,
|
private val timelineDateFormatter: TimelineDateFormatter,
|
||||||
private val colorProvider: ColorProvider,
|
private val colorProvider: ColorProvider,
|
||||||
|
private val stringProvider: StringProvider,
|
||||||
private val avatarRenderer: AvatarRenderer) {
|
private val avatarRenderer: AvatarRenderer) {
|
||||||
|
|
||||||
fun create(roomSummary: RoomSummary, onRoomSelected: (RoomSummary) -> Unit): RoomSummaryItem {
|
fun create(roomSummary: RoomSummary, listener: RoomSummaryController.Listener?): VectorEpoxyModel<*> {
|
||||||
|
return when (roomSummary.membership) {
|
||||||
|
Membership.INVITE -> createInvitationItem(roomSummary, listener)
|
||||||
|
else -> createRoomItem(roomSummary, listener)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createInvitationItem(roomSummary: RoomSummary, listener: RoomSummaryController.Listener?): VectorEpoxyModel<*> {
|
||||||
|
val secondLine = if (roomSummary.isDirect) {
|
||||||
|
roomSummary.latestEvent?.root?.senderId
|
||||||
|
} else {
|
||||||
|
roomSummary.latestEvent?.root?.senderId?.let {
|
||||||
|
stringProvider.getString(R.string.invited_by, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return RoomInvitationItem_()
|
||||||
|
.id(roomSummary.roomId)
|
||||||
|
.avatarRenderer(avatarRenderer)
|
||||||
|
.roomId(roomSummary.roomId)
|
||||||
|
.secondLine(secondLine)
|
||||||
|
.acceptListener { listener?.onAcceptRoomInvitation(roomSummary) }
|
||||||
|
.rejectListener { listener?.onRejectRoomInvitation(roomSummary) }
|
||||||
|
.roomName(roomSummary.displayName)
|
||||||
|
.avatarUrl(roomSummary.avatarUrl)
|
||||||
|
.listener { listener?.onRoomSelected(roomSummary) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createRoomItem(roomSummary: RoomSummary, listener: RoomSummaryController.Listener?): VectorEpoxyModel<*> {
|
||||||
val unreadCount = roomSummary.notificationCount
|
val unreadCount = roomSummary.notificationCount
|
||||||
val showHighlighted = roomSummary.highlightCount > 0
|
val showHighlighted = roomSummary.highlightCount > 0
|
||||||
|
|
||||||
@ -84,6 +117,7 @@ class RoomSummaryItemFactory @Inject constructor(private val noticeEventFormatte
|
|||||||
.avatarUrl(roomSummary.avatarUrl)
|
.avatarUrl(roomSummary.avatarUrl)
|
||||||
.showHighlighted(showHighlighted)
|
.showHighlighted(showHighlighted)
|
||||||
.unreadCount(unreadCount)
|
.unreadCount(unreadCount)
|
||||||
.listener { onRoomSelected(roomSummary) }
|
.listener { listener?.onRoomSelected(roomSummary) }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
111
vector/src/main/res/layout/item_room_invitation.xml
Normal file
111
vector/src/main/res/layout/item_room_invitation.xml
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout 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/itemRoomInvitationLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?riotx_background"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:foreground="?attr/selectableItemBackground">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/roomInvitationAvatarImageView"
|
||||||
|
android:layout_width="56dp"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:src="@tools:sample/avatars" />
|
||||||
|
|
||||||
|
<!-- Margin bottom does not work, so I use space -->
|
||||||
|
<Space
|
||||||
|
android:id="@+id/roomInvitationAvatarBottomSpace"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="12dp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/roomInvitationAvatarImageView"
|
||||||
|
tools:layout_marginStart="20dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/roomInvitationNameView"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/layout_horizontal_margin"
|
||||||
|
android:layout_marginLeft="@dimen/layout_horizontal_margin"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:layout_marginEnd="20dp"
|
||||||
|
android:layout_marginRight="20dp"
|
||||||
|
android:drawableEnd="@drawable/ic_arrow_right"
|
||||||
|
android:drawablePadding="8dp"
|
||||||
|
android:duplicateParentState="true"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="?riotx_text_primary"
|
||||||
|
android:textSize="15sp"
|
||||||
|
app:layout_constrainedWidth="true"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
|
app:layout_constraintHorizontal_chainStyle="packed"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/roomInvitationAvatarImageView"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="@sample/matrix.json/data/displayName" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/roomInvitationSubTitle"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="3dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:textColor="?riotx_text_secondary"
|
||||||
|
android:textSize="15sp"
|
||||||
|
app:layout_constraintEnd_toEndOf="@+id/roomInvitationNameView"
|
||||||
|
app:layout_constraintStart_toStartOf="@+id/roomInvitationNameView"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/roomInvitationNameView"
|
||||||
|
tools:text="@sample/matrix.json/data/message" />
|
||||||
|
|
||||||
|
<!-- Margin bottom does not work, so I use space -->
|
||||||
|
<Space
|
||||||
|
android:id="@+id/roomLastEventBottomSpace"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="7dp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/roomInvitationSubTitle"
|
||||||
|
tools:layout_marginStart="120dp" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/roomInvitationAccept"
|
||||||
|
style="@style/VectorButtonStyle"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:minWidth="122dp"
|
||||||
|
android:text="@string/accept"
|
||||||
|
app:layout_constraintEnd_toEndOf="@+id/roomInvitationNameView"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/roomLastEventBottomSpace" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/roomInvitationReject"
|
||||||
|
style="@style/VectorButtonStyleOutlined"
|
||||||
|
android:layout_marginEnd="@dimen/layout_vertical_margin"
|
||||||
|
android:minWidth="122dp"
|
||||||
|
android:text="@string/reject"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
android:textColor="?riotx_text_primary"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/roomInvitationAccept"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/roomInvitationAccept" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/roomInvitationDividerView"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginTop="5dp"
|
||||||
|
android:background="?riotx_header_panel_border_mobile"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/roomInvitationAccept" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -142,6 +142,16 @@
|
|||||||
<item name="colorControlHighlight">?colorAccent</item>
|
<item name="colorControlHighlight">?colorAccent</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="VectorButtonStyleOutlined" parent="Widget.MaterialComponents.Button.OutlinedButton">
|
||||||
|
<item name="android:textStyle">bold</item>
|
||||||
|
<item name="android:textAllCaps">false</item>
|
||||||
|
<item name="android:layout_width">wrap_content</item>
|
||||||
|
<item name="android:layout_height">wrap_content</item>
|
||||||
|
<item name="android:background">@null</item>
|
||||||
|
<!--item name="android:textColor">?colorAccent</item-->
|
||||||
|
<item name="colorControlHighlight">?colorAccent</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
<style name="AlerterButton" parent="Widget.AppCompat.Button.Borderless.Colored">
|
<style name="AlerterButton" parent="Widget.AppCompat.Button.Borderless.Colored">
|
||||||
<item name="colorAccent">@android:color/white</item>
|
<item name="colorAccent">@android:color/white</item>
|
||||||
<item name="android:textColor">@android:color/white</item>
|
<item name="android:textColor">@android:color/white</item>
|
||||||
|
Loading…
Reference in New Issue
Block a user