Merge pull request #891 from vector-im/feature/event-unknown

Feature/event unknown
This commit is contained in:
Benoit Marty 2020-01-28 17:30:02 +01:00 committed by GitHub
commit e12de3fba0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 133 additions and 67 deletions

View file

@ -46,18 +46,15 @@ class MessageActionsEpoxyController @Inject constructor(
override fun buildModels(state: MessageActionState) {
// Message preview
val body = state.messageBody
if (body != null) {
bottomSheetMessagePreviewItem {
id("preview")
avatarRenderer(avatarRenderer)
matrixItem(state.informationData.matrixItem)
movementMethod(createLinkMovementMethod(listener))
userClicked { listener?.didSelectMenuAction(EventSharedAction.OpenUserProfile(state.informationData.senderId)) }
body(body.linkify(listener))
body(state.messageBody.linkify(listener))
time(state.time())
}
}
// Send state
if (state.informationData.sendState.isSending()) {

View file

@ -15,7 +15,12 @@
*/
package im.vector.riotx.features.home.room.detail.timeline.action
import com.airbnb.mvrx.*
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import dagger.Lazy
@ -45,7 +50,8 @@ import im.vector.riotx.features.html.VectorHtmlCompressor
import im.vector.riotx.features.reactions.data.EmojiDataSource
import im.vector.riotx.features.settings.VectorPreferences
import java.text.SimpleDateFormat
import java.util.*
import java.util.Date
import java.util.Locale
/**
* Quick reactions state
@ -60,7 +66,7 @@ data class MessageActionState(
val eventId: String,
val informationData: MessageInformationData,
val timelineEvent: Async<TimelineEvent> = Uninitialized,
val messageBody: CharSequence? = null,
val messageBody: CharSequence = "",
// For quick reactions
val quickStates: Async<List<ToggleState>> = Uninitialized,
// For actions
@ -155,13 +161,16 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
private fun observeTimelineEventState() {
asyncSubscribe(MessageActionState::timelineEvent) { timelineEvent ->
val computedMessage = computeMessageBody(timelineEvent)
val actions = actionsForEvent(timelineEvent)
setState { copy(messageBody = computedMessage, actions = actions) }
setState {
copy(
messageBody = computeMessageBody(timelineEvent),
actions = actionsForEvent(timelineEvent)
)
}
}
}
private fun computeMessageBody(timelineEvent: TimelineEvent): CharSequence? {
private fun computeMessageBody(timelineEvent: TimelineEvent): CharSequence {
return when (timelineEvent.root.getClearType()) {
EventType.MESSAGE,
EventType.STICKER -> {
@ -189,7 +198,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
noticeEventFormatter.format(timelineEvent)
}
else -> null
}
} ?: ""
}
private fun actionsForEvent(timelineEvent: TimelineEvent): List<EventSharedAction> {

View file

@ -16,10 +16,13 @@
package im.vector.riotx.features.home.room.detail.timeline.factory
import android.view.View
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.riotx.features.home.room.detail.timeline.helper.AvatarSizeProvider
import im.vector.riotx.R
import im.vector.riotx.core.resources.StringProvider
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.helper.AvatarSizeProvider
import im.vector.riotx.features.home.room.detail.timeline.helper.MessageInformationDataFactory
import im.vector.riotx.features.home.room.detail.timeline.item.DefaultItem
import im.vector.riotx.features.home.room.detail.timeline.item.DefaultItem_
@ -28,20 +31,26 @@ import javax.inject.Inject
class DefaultItemFactory @Inject constructor(private val avatarSizeProvider: AvatarSizeProvider,
private val avatarRenderer: AvatarRenderer,
private val stringProvider: StringProvider,
private val informationDataFactory: MessageInformationDataFactory) {
fun create(text: String,
informationData: MessageInformationData,
highlight: Boolean,
callback: TimelineEventController.Callback?): DefaultItem {
val attributes = DefaultItem.Attributes(
avatarRenderer = avatarRenderer,
informationData = informationData,
text = text,
itemLongClickListener = View.OnLongClickListener { view ->
callback?.onEventLongClicked(informationData, null, view) ?: false
},
readReceiptsCallback = callback
)
return DefaultItem_()
.leftGuideline(avatarSizeProvider.leftGuideline)
.highlighted(highlight)
.text(text)
.avatarRenderer(avatarRenderer)
.informationData(informationData)
.baseCallback(callback)
.readReceiptsCallback(callback)
.attributes(attributes)
}
fun create(event: TimelineEvent,
@ -49,9 +58,9 @@ class DefaultItemFactory @Inject constructor(private val avatarSizeProvider: Ava
callback: TimelineEventController.Callback?,
throwable: Throwable? = null): DefaultItem {
val text = if (throwable == null) {
"${event.root.getClearType()} events are not yet handled"
stringProvider.getString(R.string.rendering_event_error_type_of_event_not_handled, event.root.getClearType())
} else {
"an exception occurred when rendering the event ${event.root.eventId}"
stringProvider.getString(R.string.rendering_event_error_exception, event.root.eventId)
}
val informationData = informationDataFactory.create(event, null)
return create(text, informationData, highlight, callback)

View file

@ -26,7 +26,16 @@ import android.view.View
import dagger.Lazy
import im.vector.matrix.android.api.session.events.model.RelationType
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.message.*
import im.vector.matrix.android.api.session.room.model.message.MessageAudioContent
import im.vector.matrix.android.api.session.room.model.message.MessageContent
import im.vector.matrix.android.api.session.room.model.message.MessageEmoteContent
import im.vector.matrix.android.api.session.room.model.message.MessageFileContent
import im.vector.matrix.android.api.session.room.model.message.MessageImageInfoContent
import im.vector.matrix.android.api.session.room.model.message.MessageNoticeContent
import im.vector.matrix.android.api.session.room.model.message.MessageTextContent
import im.vector.matrix.android.api.session.room.model.message.MessageType
import im.vector.matrix.android.api.session.room.model.message.MessageVideoContent
import im.vector.matrix.android.api.session.room.model.message.getFileUrl
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.api.session.room.timeline.getLastMessageContent
import im.vector.matrix.android.internal.crypto.attachments.toElementToDecrypt
@ -40,8 +49,24 @@ import im.vector.riotx.core.utils.DimensionConverter
import im.vector.riotx.core.utils.containsOnlyEmojis
import im.vector.riotx.core.utils.isLocalFile
import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController
import im.vector.riotx.features.home.room.detail.timeline.helper.*
import im.vector.riotx.features.home.room.detail.timeline.item.*
import im.vector.riotx.features.home.room.detail.timeline.helper.AvatarSizeProvider
import im.vector.riotx.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder
import im.vector.riotx.features.home.room.detail.timeline.helper.MessageInformationDataFactory
import im.vector.riotx.features.home.room.detail.timeline.helper.MessageItemAttributesFactory
import im.vector.riotx.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
import im.vector.riotx.features.home.room.detail.timeline.item.AbsMessageItem
import im.vector.riotx.features.home.room.detail.timeline.item.DefaultItem
import im.vector.riotx.features.home.room.detail.timeline.item.MessageBlockCodeItem
import im.vector.riotx.features.home.room.detail.timeline.item.MessageBlockCodeItem_
import im.vector.riotx.features.home.room.detail.timeline.item.MessageFileItem
import im.vector.riotx.features.home.room.detail.timeline.item.MessageFileItem_
import im.vector.riotx.features.home.room.detail.timeline.item.MessageImageVideoItem
import im.vector.riotx.features.home.room.detail.timeline.item.MessageImageVideoItem_
import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
import im.vector.riotx.features.home.room.detail.timeline.item.MessageTextItem
import im.vector.riotx.features.home.room.detail.timeline.item.MessageTextItem_
import im.vector.riotx.features.home.room.detail.timeline.item.RedactedMessageItem
import im.vector.riotx.features.home.room.detail.timeline.item.RedactedMessageItem_
import im.vector.riotx.features.home.room.detail.timeline.tools.createLinkMovementMethod
import im.vector.riotx.features.home.room.detail.timeline.tools.linkify
import im.vector.riotx.features.html.CodeVisitor
@ -153,7 +178,7 @@ class MessageItemFactory @Inject constructor(
informationData: MessageInformationData,
highlight: Boolean,
callback: TimelineEventController.Callback?): DefaultItem? {
val text = "${messageContent.type} message events are not yet handled"
val text = stringProvider.getString(R.string.rendering_event_error_type_of_message_not_handled, messageContent.type)
return defaultItemFactory.create(text, informationData, highlight, callback)
}

View file

@ -18,6 +18,7 @@ package im.vector.riotx.features.home.room.detail.timeline.item
import android.view.View
import android.view.ViewStub
import android.widget.RelativeLayout
import androidx.annotation.CallSuper
import androidx.annotation.IdRes
import androidx.core.view.updateLayoutParams
import com.airbnb.epoxy.EpoxyAttribute
@ -42,6 +43,7 @@ abstract class BaseEventItem<H : BaseEventItem.BaseHolder> : VectorEpoxyModel<H>
@EpoxyAttribute
lateinit var dimensionConverter: DimensionConverter
@CallSuper
override fun bind(holder: H) {
super.bind(holder)
holder.leftGuideline.updateLayoutParams<RelativeLayout.LayoutParams> {

View file

@ -17,6 +17,7 @@
package im.vector.riotx.features.home.room.detail.timeline.item
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
@ -29,42 +30,39 @@ import im.vector.riotx.features.home.room.detail.timeline.TimelineEventControlle
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
lateinit var attributes: Attributes
private val _readReceiptsClickListener = DebouncedClickListener(View.OnClickListener {
readReceiptsCallback?.onReadReceiptsClicked(informationData.readReceipts)
attributes.readReceiptsCallback?.onReadReceiptsClicked(attributes.informationData.readReceipts)
})
@EpoxyAttribute
var text: CharSequence? = null
override fun bind(holder: Holder) {
holder.messageView.text = text
holder.view.setOnLongClickListener(longClickListener)
holder.readReceiptsView.render(informationData.readReceipts, avatarRenderer, _readReceiptsClickListener)
super.bind(holder)
holder.messageTextView.text = attributes.text
attributes.avatarRenderer.render(attributes.informationData.matrixItem, holder.avatarImageView)
holder.view.setOnLongClickListener(attributes.itemLongClickListener)
holder.readReceiptsView.render(attributes.informationData.readReceipts, attributes.avatarRenderer, _readReceiptsClickListener)
}
override fun getEventIds(): List<String> {
return listOf(informationData.eventId)
return listOf(attributes.informationData.eventId)
}
override fun getViewType() = STUB_ID
class Holder : BaseHolder(STUB_ID) {
val messageView by bind<TextView>(R.id.stateMessageView)
val avatarImageView by bind<ImageView>(R.id.itemDefaultAvatarView)
val messageTextView by bind<TextView>(R.id.itemDefaultTextView)
}
data class Attributes(
val avatarRenderer: AvatarRenderer,
val informationData: MessageInformationData,
val text: CharSequence,
val itemLongClickListener: View.OnLongClickListener? = null,
val readReceiptsCallback: TimelineEventController.ReadReceiptsCallback? = null
)
companion object {
private const val STUB_ID = R.id.messageContentDefaultStub
}

View file

@ -36,8 +36,9 @@
<ViewStub
android:id="@+id/messageContentDefaultStub"
style="@style/TimelineContentStubBaseParams"
android:inflatedId="@+id/stateMessageView"
android:layout="@layout/item_timeline_event_default_stub" />
android:layout="@layout/item_timeline_event_default_stub"
tools:layout_marginTop="80dp"
tools:visibility="visible" />
<ViewStub
android:id="@+id/messageContentBlankStub"
@ -49,7 +50,9 @@
<ViewStub
android:id="@+id/messageContentMergedHeaderStub"
style="@style/TimelineContentStubBaseParams"
android:layout="@layout/item_timeline_event_merged_header_stub" />
android:layout="@layout/item_timeline_event_merged_header_stub"
tools:layout_marginTop="160dp"
tools:visibility="visible" />
</FrameLayout>

View file

@ -1,12 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout 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/stateMessageView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:padding="8dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/itemDefaultAvatarView"
android:layout_width="24dp"
android:layout_height="24dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:srcCompat="@tools:sample/avatars" />
<TextView
android:id="@+id/itemDefaultTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:textColor="?attr/colorAccent"
android:textSize="14sp"
android:textStyle="italic"
tools:text="Mon item" />
tools:text="@string/rendering_event_error_type_of_event_not_handled" />
</LinearLayout>

View file

@ -3,8 +3,8 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:orientation="horizontal"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/itemNoticeAvatarView"
@ -15,16 +15,16 @@
tools:srcCompat="@tools:sample/avatars" />
<TextView
android:layout_gravity="top"
android:id="@+id/itemNoticeTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:textColor="?riotx_text_secondary"
android:textSize="14sp"
android:textStyle="italic"
tools:text="John doe changed their avatar" />
tools:text="@string/notice_avatar_url_changed" />
</LinearLayout>

View file

@ -32,6 +32,10 @@
<string name="room_member_jump_to_read_receipt">Jump to read receipt</string>
<string name="rendering_event_error_type_of_event_not_handled">"RiotX does not handle events of type '%1$s' (yet)"</string>
<string name="rendering_event_error_type_of_message_not_handled">"RiotX does not handle message of type '%1$s' (yet)"</string>
<string name="rendering_event_error_exception">"RiotX encountered an issue when rendering content of event with id '%1$s'"</string>
<string name="unignore">Unignore</string>
<string name="room_list_sharing_header_recent_rooms">Recent rooms</string>