forked from GitHub-Mirror/riotX-android
Fix / Bug aggregation on initial sync
fix / All messages were not processed due to a test exiting the for loop + started adding context menu for non room messages
This commit is contained in:
@ -563,11 +563,11 @@ class RoomDetailFragment :
|
||||
vectorBaseActivity.notImplemented()
|
||||
}
|
||||
|
||||
override fun onEventCellClicked(informationData: MessageInformationData, messageContent: MessageContent, view: View) {
|
||||
override fun onEventCellClicked(informationData: MessageInformationData, messageContent: MessageContent?, view: View) {
|
||||
|
||||
}
|
||||
|
||||
override fun onEventLongClicked(informationData: MessageInformationData, messageContent: MessageContent, view: View): Boolean {
|
||||
override fun onEventLongClicked(informationData: MessageInformationData, messageContent: MessageContent?, view: View): Boolean {
|
||||
view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
|
||||
val roomId = roomDetailArgs.roomId
|
||||
|
||||
|
@ -55,7 +55,8 @@ class RoomDetailViewModel(initialState: RoomDetailViewState,
|
||||
private val roomId = initialState.roomId
|
||||
private val eventId = initialState.eventId
|
||||
private val displayedEventsObservable = BehaviorRelay.create<RoomDetailActions.EventDisplayed>()
|
||||
private val timeline = room.createTimeline(eventId, TimelineDisplayableEvents.DISPLAYABLE_TYPES)
|
||||
private val timeline = room.createTimeline(eventId,
|
||||
if (TimelineDisplayableEvents.DEBUG_HIDDEN_EVENT) TimelineDisplayableEvents.DEBUG_DISPLAYABLE_TYPES else TimelineDisplayableEvents.DISPLAYABLE_TYPES)
|
||||
|
||||
companion object : MvRxViewModelFactory<RoomDetailViewModel, RoomDetailViewState> {
|
||||
|
||||
|
@ -46,15 +46,13 @@ class TimelineEventController(private val dateFormatter: TimelineDateFormatter,
|
||||
private val backgroundHandler: Handler = TimelineAsyncHelper.getBackgroundHandler()
|
||||
) : EpoxyController(backgroundHandler, backgroundHandler), Timeline.Listener {
|
||||
|
||||
interface Callback : ReactionPillCallback, AvatarCallback {
|
||||
interface Callback : ReactionPillCallback, AvatarCallback, BaseCallback {
|
||||
fun onEventVisible(event: TimelineEvent)
|
||||
fun onUrlClicked(url: String)
|
||||
fun onImageMessageClicked(messageImageContent: MessageImageContent, mediaData: ImageContentRenderer.Data, view: View)
|
||||
fun onVideoMessageClicked(messageVideoContent: MessageVideoContent, mediaData: VideoContentRenderer.Data, view: View)
|
||||
fun onFileMessageClicked(messageFileContent: MessageFileContent)
|
||||
fun onAudioMessageClicked(messageAudioContent: MessageAudioContent)
|
||||
fun onEventCellClicked(informationData: MessageInformationData, messageContent: MessageContent, view: View)
|
||||
fun onEventLongClicked(informationData: MessageInformationData, messageContent: MessageContent, view: View): Boolean
|
||||
fun onEditedDecorationClicked(informationData: MessageInformationData, editAggregatedSummary: EditAggregatedSummary?)
|
||||
}
|
||||
|
||||
@ -63,6 +61,11 @@ class TimelineEventController(private val dateFormatter: TimelineDateFormatter,
|
||||
fun onLongClickOnReactionPill(informationData: MessageInformationData, reaction: String)
|
||||
}
|
||||
|
||||
interface BaseCallback {
|
||||
fun onEventCellClicked(informationData: MessageInformationData, messageContent: MessageContent?, view: View)
|
||||
fun onEventLongClicked(informationData: MessageInformationData, messageContent: MessageContent?, view: View): Boolean
|
||||
}
|
||||
|
||||
interface AvatarCallback {
|
||||
fun onAvatarClicked(informationData: MessageInformationData)
|
||||
fun onMemberNameClicked(informationData: MessageInformationData)
|
||||
|
@ -34,7 +34,7 @@ import org.koin.android.ext.android.get
|
||||
|
||||
data class SimpleAction(val uid: String, val titleRes: Int, val iconResId: Int?, val data: Any? = null)
|
||||
|
||||
data class MessageMenuState(val actions: List<SimpleAction>) : MvRxState
|
||||
data class MessageMenuState(val actions: List<SimpleAction> = emptyList()) : MvRxState
|
||||
|
||||
/**
|
||||
* Manages list actions for a given message (copy / paste / forward...)
|
||||
@ -50,9 +50,9 @@ class MessageMenuViewModel(initialState: MessageMenuState) : VectorViewModel<Mes
|
||||
val event = currentSession.getRoom(parcel.roomId)?.getTimeLineEvent(parcel.eventId)
|
||||
?: return null
|
||||
|
||||
val messageContent: MessageContent = event.annotations?.editSummary?.aggregatedContent?.toModel()
|
||||
?: event.root.content.toModel() ?: return null
|
||||
val type = messageContent.type
|
||||
val messageContent: MessageContent? = event.annotations?.editSummary?.aggregatedContent?.toModel()
|
||||
?: event.root.content.toModel()
|
||||
val type = messageContent?.type
|
||||
|
||||
if (event.sendState == SendState.UNSENT) {
|
||||
//Resend and Delete
|
||||
@ -76,7 +76,7 @@ class MessageMenuViewModel(initialState: MessageMenuState) : VectorViewModel<Mes
|
||||
this.add(SimpleAction(ACTION_ADD_REACTION, R.string.message_add_reaction, R.drawable.ic_add_reaction, event.root.eventId))
|
||||
if (canCopy(type)) {
|
||||
//TODO copy images? html? see ClipBoard
|
||||
this.add(SimpleAction(ACTION_COPY, R.string.copy, R.drawable.ic_copy, messageContent.body))
|
||||
this.add(SimpleAction(ACTION_COPY, R.string.copy, R.drawable.ic_copy, messageContent!!.body))
|
||||
}
|
||||
|
||||
if (canReply(event, messageContent)) {
|
||||
@ -134,10 +134,10 @@ class MessageMenuViewModel(initialState: MessageMenuState) : VectorViewModel<Mes
|
||||
return MessageMenuState(actions)
|
||||
}
|
||||
|
||||
private fun canReply(event: TimelineEvent, messageContent: MessageContent): Boolean {
|
||||
private fun canReply(event: TimelineEvent, messageContent: MessageContent?): Boolean {
|
||||
//Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment
|
||||
if (event.root.type != EventType.MESSAGE) return false
|
||||
return when (messageContent.type) {
|
||||
return when (messageContent?.type) {
|
||||
MessageType.MSGTYPE_TEXT,
|
||||
MessageType.MSGTYPE_NOTICE,
|
||||
MessageType.MSGTYPE_EMOTE,
|
||||
@ -149,10 +149,10 @@ class MessageMenuViewModel(initialState: MessageMenuState) : VectorViewModel<Mes
|
||||
}
|
||||
}
|
||||
|
||||
private fun canQuote(event: TimelineEvent, messageContent: MessageContent): Boolean {
|
||||
private fun canQuote(event: TimelineEvent, messageContent: MessageContent?): Boolean {
|
||||
//Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment
|
||||
if (event.root.type != EventType.MESSAGE) return false
|
||||
return when (messageContent.type) {
|
||||
return when (messageContent?.type) {
|
||||
MessageType.MSGTYPE_TEXT,
|
||||
MessageType.MSGTYPE_NOTICE,
|
||||
MessageType.MSGTYPE_EMOTE,
|
||||
@ -190,7 +190,7 @@ class MessageMenuViewModel(initialState: MessageMenuState) : VectorViewModel<Mes
|
||||
}
|
||||
|
||||
|
||||
private fun canCopy(type: String): Boolean {
|
||||
private fun canCopy(type: String?): Boolean {
|
||||
return when (type) {
|
||||
MessageType.MSGTYPE_TEXT,
|
||||
MessageType.MSGTYPE_NOTICE,
|
||||
@ -204,7 +204,7 @@ class MessageMenuViewModel(initialState: MessageMenuState) : VectorViewModel<Mes
|
||||
}
|
||||
|
||||
|
||||
private fun canShare(type: String): Boolean {
|
||||
private fun canShare(type: String?): Boolean {
|
||||
return when (type) {
|
||||
MessageType.MSGTYPE_IMAGE,
|
||||
MessageType.MSGTYPE_AUDIO,
|
||||
|
@ -17,23 +17,32 @@
|
||||
package im.vector.riotredesign.features.home.room.detail.timeline.factory
|
||||
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.format.NoticeEventFormatter
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.senderAvatar
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.senderName
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem_
|
||||
|
||||
class NoticeItemFactory(private val eventFormatter: NoticeEventFormatter) {
|
||||
|
||||
fun create(event: TimelineEvent): NoticeItem? {
|
||||
fun create(event: TimelineEvent,
|
||||
callback: TimelineEventController.Callback?): NoticeItem? {
|
||||
val formattedText = eventFormatter.format(event) ?: return null
|
||||
val senderName = event.senderName()
|
||||
val senderAvatar = event.senderAvatar()
|
||||
val informationData = MessageInformationData(
|
||||
eventId = event.root.eventId ?: "?",
|
||||
senderId = event.root.sender ?: "",
|
||||
sendState = event.sendState,
|
||||
avatarUrl = event.senderAvatar(),
|
||||
memberName = event.senderName(),
|
||||
showInformation = false
|
||||
)
|
||||
|
||||
return NoticeItem_()
|
||||
.noticeText(formattedText)
|
||||
.avatarUrl(senderAvatar)
|
||||
.memberName(senderName)
|
||||
.informationData(informationData)
|
||||
.baseCallback(callback)
|
||||
}
|
||||
|
||||
|
||||
|
@ -17,10 +17,16 @@
|
||||
package im.vector.riotredesign.features.home.room.detail.timeline.factory
|
||||
|
||||
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.room.model.message.MessageContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageDefaultContent
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import im.vector.riotredesign.core.epoxy.EmptyItem_
|
||||
import im.vector.riotredesign.core.epoxy.VectorEpoxyModel
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDisplayableEvents
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageTextItem_
|
||||
import timber.log.Timber
|
||||
|
||||
class TimelineItemFactory(private val messageItemFactory: MessageItemFactory,
|
||||
@ -43,7 +49,7 @@ class TimelineItemFactory(private val messageItemFactory: MessageItemFactory,
|
||||
EventType.STATE_HISTORY_VISIBILITY,
|
||||
EventType.CALL_INVITE,
|
||||
EventType.CALL_HANGUP,
|
||||
EventType.CALL_ANSWER -> noticeItemFactory.create(event)
|
||||
EventType.CALL_ANSWER -> noticeItemFactory.create(event, callback)
|
||||
|
||||
// Unhandled event types (yet)
|
||||
EventType.ENCRYPTED,
|
||||
@ -51,9 +57,32 @@ class TimelineItemFactory(private val messageItemFactory: MessageItemFactory,
|
||||
EventType.STATE_ROOM_THIRD_PARTY_INVITE,
|
||||
EventType.STICKER,
|
||||
EventType.STATE_ROOM_CREATE -> defaultItemFactory.create(event)
|
||||
|
||||
else -> {
|
||||
Timber.w("Ignored event (type: ${event.root.type}")
|
||||
null
|
||||
//These are just for debug to display hidden event, they should be filtered out in normal mode
|
||||
if (TimelineDisplayableEvents.DEBUG_HIDDEN_EVENT) {
|
||||
val informationData = MessageInformationData(eventId = event.root.eventId
|
||||
?: "?",
|
||||
senderId = event.root.sender ?: "",
|
||||
sendState = event.sendState,
|
||||
time = "",
|
||||
avatarUrl = null,
|
||||
memberName = "",
|
||||
showInformation = false
|
||||
)
|
||||
val messageContent = event.root.content.toModel<MessageContent>()
|
||||
?: MessageDefaultContent("", "", null, null)
|
||||
MessageTextItem_()
|
||||
.informationData(informationData)
|
||||
.message("{ \"type\": ${event.root.type} }")
|
||||
.longClickListener { view ->
|
||||
return@longClickListener callback?.onEventLongClicked(informationData, messageContent, view)
|
||||
?: false
|
||||
}
|
||||
} else {
|
||||
Timber.w("Ignored event (type: ${event.root.type}")
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
|
@ -22,10 +22,14 @@ import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.RoomMember
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageContent
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import im.vector.riotredesign.BuildConfig
|
||||
import im.vector.riotredesign.core.extensions.localDateTime
|
||||
|
||||
object TimelineDisplayableEvents {
|
||||
|
||||
//Debug helper, to show invisible items in time line (reaction, redacts)
|
||||
val DEBUG_HIDDEN_EVENT = BuildConfig.SHOW_HIDDEN_TIMELINE_EVENTS
|
||||
|
||||
val DISPLAYABLE_TYPES = listOf(
|
||||
EventType.MESSAGE,
|
||||
EventType.STATE_ROOM_NAME,
|
||||
@ -41,6 +45,11 @@ object TimelineDisplayableEvents {
|
||||
EventType.STICKER,
|
||||
EventType.STATE_ROOM_CREATE
|
||||
)
|
||||
|
||||
val DEBUG_DISPLAYABLE_TYPES = DISPLAYABLE_TYPES + listOf(
|
||||
EventType.REDACTION,
|
||||
EventType.REACTION
|
||||
)
|
||||
}
|
||||
|
||||
fun TimelineEvent.isDisplayable(): Boolean {
|
||||
|
@ -23,27 +23,37 @@ import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.features.home.AvatarRenderer
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_timeline_event_base_noinfo)
|
||||
abstract class NoticeItem : BaseEventItem<NoticeItem.Holder>() {
|
||||
|
||||
@EpoxyAttribute
|
||||
var noticeText: CharSequence? = null
|
||||
@EpoxyAttribute
|
||||
var avatarUrl: String? = null
|
||||
@EpoxyAttribute
|
||||
var userId: String = ""
|
||||
@EpoxyAttribute
|
||||
var memberName: CharSequence? = null
|
||||
|
||||
|
||||
@EpoxyAttribute
|
||||
var longClickListener: View.OnLongClickListener? = null
|
||||
lateinit var informationData: MessageInformationData
|
||||
|
||||
@EpoxyAttribute
|
||||
var baseCallback: TimelineEventController.BaseCallback? = null
|
||||
|
||||
|
||||
private var longClickListener = View.OnLongClickListener {
|
||||
baseCallback?.onEventLongClicked(informationData, null, it)
|
||||
baseCallback != null
|
||||
}
|
||||
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
holder.noticeTextView.text = noticeText
|
||||
AvatarRenderer.render(avatarUrl, userId, memberName?.toString(), holder.avatarImageView)
|
||||
AvatarRenderer.render(
|
||||
informationData.avatarUrl,
|
||||
informationData.senderId,
|
||||
informationData.memberName?.toString()
|
||||
?: informationData.senderId,
|
||||
holder.avatarImageView
|
||||
)
|
||||
holder.view.setOnLongClickListener(longClickListener)
|
||||
}
|
||||
|
||||
@ -51,7 +61,6 @@ abstract class NoticeItem : BaseEventItem<NoticeItem.Holder>() {
|
||||
|
||||
class Holder : BaseHolder() {
|
||||
override fun getStubId(): Int = STUB_ID
|
||||
|
||||
val avatarImageView by bind<ImageView>(R.id.itemNoticeAvatarView)
|
||||
val noticeTextView by bind<TextView>(R.id.itemNoticeTextView)
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:addStatesFromChildren="true"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingRight="8dp">
|
||||
|
||||
@ -31,9 +32,9 @@
|
||||
<ViewStub
|
||||
android:id="@+id/messageContentBlankStub"
|
||||
style="@style/TimelineContentStubNoInfoLayoutParams"
|
||||
android:layout="@layout/item_timeline_event_blank_stub"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout="@layout/item_timeline_event_blank_stub"
|
||||
tools:ignore="MissingConstraints" />
|
||||
|
||||
<ViewStub
|
||||
|
Reference in New Issue
Block a user