Merge pull request #525 from vector-im/feature/read_receipt_cleanup

Feature/read receipt cleanup
This commit is contained in:
Benoit Marty 2019-08-29 10:19:06 +02:00 committed by GitHub
commit 188a9aebfa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 57 additions and 19 deletions

View File

@ -21,11 +21,8 @@ import android.util.AttributeSet
import android.view.View import android.view.View
import android.widget.ImageView import android.widget.ImageView
import android.widget.LinearLayout import android.widget.LinearLayout
import androidx.core.view.isInvisible
import androidx.core.view.isVisible import androidx.core.view.isVisible
import butterknife.ButterKnife
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.utils.DebouncedClickListener
import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.home.AvatarRenderer
import im.vector.riotx.features.home.room.detail.timeline.item.ReadReceiptData import im.vector.riotx.features.home.room.detail.timeline.item.ReadReceiptData
import kotlinx.android.synthetic.main.view_read_receipts.view.* import kotlinx.android.synthetic.main.view_read_receipts.view.*
@ -48,7 +45,6 @@ class ReadReceiptsView @JvmOverloads constructor(


private fun setupView() { private fun setupView() {
inflate(context, R.layout.view_read_receipts, this) inflate(context, R.layout.view_read_receipts, this)
ButterKnife.bind(this)
} }


fun render(readReceipts: List<ReadReceiptData>, avatarRenderer: AvatarRenderer, clickListener: OnClickListener) { fun render(readReceipts: List<ReadReceiptData>, avatarRenderer: AvatarRenderer, clickListener: OnClickListener) {

View File

@ -17,21 +17,35 @@
package im.vector.riotx.features.home.room.detail.timeline.factory package im.vector.riotx.features.home.room.detail.timeline.factory


import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.riotx.features.home.AvatarRenderer
import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController
import im.vector.riotx.features.home.room.detail.timeline.item.DefaultItem import im.vector.riotx.features.home.room.detail.timeline.item.DefaultItem
import im.vector.riotx.features.home.room.detail.timeline.item.DefaultItem_ import im.vector.riotx.features.home.room.detail.timeline.item.DefaultItem_
import im.vector.riotx.features.home.room.detail.timeline.util.MessageInformationDataFactory
import javax.inject.Inject import javax.inject.Inject


class DefaultItemFactory @Inject constructor(){ class DefaultItemFactory @Inject constructor(private val avatarRenderer: AvatarRenderer,
private val informationDataFactory: MessageInformationDataFactory) {


fun create(event: TimelineEvent, highlight: Boolean, exception: Exception? = null): DefaultItem? { fun create(event: TimelineEvent,
highlight: Boolean,
callback: TimelineEventController.Callback?,
exception: Exception? = null): DefaultItem? {
val text = if (exception == null) { val text = if (exception == null) {
"${event.root.getClearType()} events are not yet handled" "${event.root.getClearType()} events are not yet handled"
} else { } else {
"an exception occurred when rendering the event ${event.root.eventId}" "an exception occurred when rendering the event ${event.root.eventId}"
} }

val informationData = informationDataFactory.create(event, null)

return DefaultItem_() return DefaultItem_()
.text(text) .text(text)
.avatarRenderer(avatarRenderer)
.highlighted(highlight) .highlighted(highlight)
.informationData(informationData)
.baseCallback(callback)
.readReceiptsCallback(callback)
} }


} }

View File

@ -20,12 +20,7 @@ import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.riotx.core.epoxy.EmptyItem_ import im.vector.riotx.core.epoxy.EmptyItem_
import im.vector.riotx.core.epoxy.VectorEpoxyModel import im.vector.riotx.core.epoxy.VectorEpoxyModel
import im.vector.riotx.features.home.AvatarRenderer
import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController
import im.vector.riotx.features.home.room.detail.timeline.helper.senderAvatar
import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
import im.vector.riotx.features.home.room.detail.timeline.item.NoticeItem_
import im.vector.riotx.features.home.room.detail.timeline.util.MessageInformationDataFactory
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject


@ -71,7 +66,7 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me


// Unhandled event types (yet) // Unhandled event types (yet)
EventType.STATE_ROOM_THIRD_PARTY_INVITE, EventType.STATE_ROOM_THIRD_PARTY_INVITE,
EventType.STICKER -> defaultItemFactory.create(event, highlight) EventType.STICKER -> defaultItemFactory.create(event, highlight, callback)
else -> { else -> {
Timber.v("Type ${event.root.getClearType()} not handled") Timber.v("Type ${event.root.getClearType()} not handled")
null null
@ -79,7 +74,7 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me
} }
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e, "failed to create message item") Timber.e(e, "failed to create message item")
defaultItemFactory.create(event, highlight, e) defaultItemFactory.create(event, highlight, callback, e)
} }
return (computedModel ?: EmptyItem_()) return (computedModel ?: EmptyItem_())
} }

View File

@ -32,7 +32,6 @@ import com.airbnb.epoxy.EpoxyAttribute
import im.vector.matrix.android.api.session.room.send.SendState import im.vector.matrix.android.api.session.room.send.SendState
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.resources.ColorProvider import im.vector.riotx.core.resources.ColorProvider
import im.vector.riotx.core.ui.views.ReadReceiptsView
import im.vector.riotx.core.utils.DebouncedClickListener import im.vector.riotx.core.utils.DebouncedClickListener
import im.vector.riotx.core.utils.DimensionUtils.dpToPx import im.vector.riotx.core.utils.DimensionUtils.dpToPx
import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.home.AvatarRenderer
@ -181,7 +180,6 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : BaseEventItem<H>() {
val avatarImageView by bind<ImageView>(R.id.messageAvatarImageView) val avatarImageView by bind<ImageView>(R.id.messageAvatarImageView)
val memberNameView by bind<TextView>(R.id.messageMemberNameView) val memberNameView by bind<TextView>(R.id.messageMemberNameView)
val timeView by bind<TextView>(R.id.messageTimeView) val timeView by bind<TextView>(R.id.messageTimeView)
val readReceiptsView by bind<ReadReceiptsView>(R.id.readReceiptsView)
var reactionWrapper: ViewGroup? = null var reactionWrapper: ViewGroup? = null
var reactionFlowHelper: Flow? = null var reactionFlowHelper: Flow? = null
} }

View File

@ -24,6 +24,7 @@ import im.vector.riotx.R
import im.vector.riotx.core.epoxy.VectorEpoxyHolder import im.vector.riotx.core.epoxy.VectorEpoxyHolder
import im.vector.riotx.core.epoxy.VectorEpoxyModel import im.vector.riotx.core.epoxy.VectorEpoxyModel
import im.vector.riotx.core.platform.CheckableView import im.vector.riotx.core.platform.CheckableView
import im.vector.riotx.core.ui.views.ReadReceiptsView
import im.vector.riotx.core.utils.DimensionUtils.dpToPx import im.vector.riotx.core.utils.DimensionUtils.dpToPx


/** /**
@ -49,6 +50,7 @@ abstract class BaseEventItem<H : BaseEventItem.BaseHolder> : VectorEpoxyModel<H>
abstract class BaseHolder(@IdRes val stubId: Int) : VectorEpoxyHolder() { abstract class BaseHolder(@IdRes val stubId: Int) : VectorEpoxyHolder() {
val leftGuideline by bind<Guideline>(R.id.messageStartGuideline) val leftGuideline by bind<Guideline>(R.id.messageStartGuideline)
val checkableBackground by bind<CheckableView>(R.id.messageSelectedBackground) val checkableBackground by bind<CheckableView>(R.id.messageSelectedBackground)
val readReceiptsView by bind<ReadReceiptsView>(R.id.readReceiptsView)


override fun bindView(itemView: View) { override fun bindView(itemView: View) {
super.bindView(itemView) super.bindView(itemView)

View File

@ -16,19 +16,46 @@


package im.vector.riotx.features.home.room.detail.timeline.item package im.vector.riotx.features.home.room.detail.timeline.item


import android.view.View
import android.widget.TextView import android.widget.TextView
import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass import com.airbnb.epoxy.EpoxyModelClass
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.utils.DebouncedClickListener
import im.vector.riotx.features.home.AvatarRenderer
import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController


@EpoxyModelClass(layout = R.layout.item_timeline_event_base_noinfo) @EpoxyModelClass(layout = R.layout.item_timeline_event_base_noinfo)
abstract class DefaultItem : BaseEventItem<DefaultItem.Holder>() { abstract class DefaultItem : BaseEventItem<DefaultItem.Holder>() {


@EpoxyAttribute
lateinit var informationData: MessageInformationData

@EpoxyAttribute
lateinit var avatarRenderer: AvatarRenderer

@EpoxyAttribute
var baseCallback: TimelineEventController.BaseCallback? = null

private var longClickListener = View.OnLongClickListener {
return@OnLongClickListener baseCallback?.onEventLongClicked(informationData, null, it) == true
}

@EpoxyAttribute
var readReceiptsCallback: TimelineEventController.ReadReceiptsCallback? = null

private val _readReceiptsClickListener = DebouncedClickListener(View.OnClickListener {
readReceiptsCallback?.onReadReceiptsClicked(informationData.readReceipts)
})

@EpoxyAttribute @EpoxyAttribute
var text: CharSequence? = null var text: CharSequence? = null


override fun bind(holder: Holder) { override fun bind(holder: Holder) {
holder.messageView.text = text holder.messageView.text = text

holder.view.setOnLongClickListener(longClickListener)
holder.readReceiptsView.render(informationData.readReceipts, avatarRenderer, _readReceiptsClickListener)
} }


override fun getViewType() = STUB_ID override fun getViewType() = STUB_ID

View File

@ -21,6 +21,7 @@ import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.core.view.children import androidx.core.view.children
import androidx.core.view.isVisible
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.home.AvatarRenderer


@ -75,6 +76,9 @@ data class MergedHeaderItem(private val isCollapsed: Boolean,
holder.separatorView.visibility = View.VISIBLE holder.separatorView.visibility = View.VISIBLE
holder.expandView.setText(R.string.merged_events_collapse) holder.expandView.setText(R.string.merged_events_collapse)
} }

// No read receipt for this item
holder.readReceiptsView.isVisible = false
} }


data class Data( data class Data(

View File

@ -22,7 +22,6 @@ import android.widget.TextView
import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass import com.airbnb.epoxy.EpoxyModelClass
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.ui.views.ReadReceiptsView
import im.vector.riotx.core.utils.DebouncedClickListener import im.vector.riotx.core.utils.DebouncedClickListener
import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.home.AvatarRenderer
import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController
@ -72,7 +71,6 @@ abstract class NoticeItem : BaseEventItem<NoticeItem.Holder>() {
class Holder : BaseHolder(STUB_ID) { class Holder : BaseHolder(STUB_ID) {
val avatarImageView by bind<ImageView>(R.id.itemNoticeAvatarView) val avatarImageView by bind<ImageView>(R.id.itemNoticeAvatarView)
val noticeTextView by bind<TextView>(R.id.itemNoticeTextView) val noticeTextView by bind<TextView>(R.id.itemNoticeTextView)
val readReceiptsView by bind<ReadReceiptsView>(R.id.readReceiptsView)
} }


companion object { companion object {

View File

@ -129,8 +129,10 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:layout_marginBottom="4dp" android:layout_marginBottom="4dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" /> app:layout_constraintEnd_toEndOf="parent"
tools:visibility="visible" />




</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -58,8 +58,10 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:layout_marginBottom="4dp" android:layout_marginBottom="4dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" /> app:layout_constraintEnd_toEndOf="parent"
tools:visibility="visible" />




</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>