mirror of
https://github.com/element-hq/element-android
synced 2024-11-25 02:45:37 +03:00
Merge pull request #891 from vector-im/feature/event-unknown
Feature/event unknown
This commit is contained in:
commit
e12de3fba0
10 changed files with 133 additions and 67 deletions
|
@ -46,17 +46,14 @@ 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))
|
||||
time(state.time())
|
||||
}
|
||||
bottomSheetMessagePreviewItem {
|
||||
id("preview")
|
||||
avatarRenderer(avatarRenderer)
|
||||
matrixItem(state.informationData.matrixItem)
|
||||
movementMethod(createLinkMovementMethod(listener))
|
||||
userClicked { listener?.didSelectMenuAction(EventSharedAction.OpenUserProfile(state.informationData.senderId)) }
|
||||
body(state.messageBody.linkify(listener))
|
||||
time(state.time())
|
||||
}
|
||||
|
||||
// Send state
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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:textColor="?attr/colorAccent"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="italic"
|
||||
tools:text="Mon item" />
|
||||
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="@string/rendering_event_error_type_of_event_not_handled" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue