[Message actions] New UI for replies (#545)
* Add 'reply to' UI to the message composer. * Move the `BlurHashAsyncImage` to `:libraries:designsystem` as it is now used in several modules. * Create reusable `AttachmentThumbnail` and associated data classes and enums, it's now added to `:libraries:matrixui`. * Re-use `AttachmentThumbnail` in a `ActionListView` and `TextComposer`. * Add 'inReplyTo' models and UI. * Add min size for images * Create a separate layout for media items with no reply to info. Also, separate `Timeline__Row` components from `TimelineView`, as it was getting too large. * Added `EqualWidthColumn` to use inside message bubbles. Also fixed some modifiers for media items replying to other messages. * Disable `inReplyToClicked`. * Remove unused resources and libraries. * Remove any traces of `BlurHashAsyncImage` in `:features:messages`, since it was moved to the design system. --------- Co-authored-by: ElementBot <benoitm+elementbot@element.io>
This commit is contained in:
parent
1ea4e96497
commit
c176eab4a3
56 changed files with 1253 additions and 1008 deletions
|
|
@ -16,12 +16,19 @@
|
|||
|
||||
package io.element.android.libraries.matrix.impl.timeline
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
||||
import io.element.android.libraries.matrix.impl.timeline.item.event.EventTimelineItemMapper
|
||||
import io.element.android.libraries.matrix.impl.timeline.item.virtual.VirtualTimelineItemMapper
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.rustcomponents.sdk.Room
|
||||
import org.matrix.rustcomponents.sdk.TimelineItem
|
||||
import timber.log.Timber
|
||||
|
||||
class MatrixTimelineItemMapper(
|
||||
private val room: Room,
|
||||
private val coroutineScope: CoroutineScope,
|
||||
private val virtualTimelineItemMapper: VirtualTimelineItemMapper = VirtualTimelineItemMapper(),
|
||||
private val eventTimelineItemMapper: EventTimelineItemMapper= EventTimelineItemMapper(),
|
||||
) {
|
||||
|
|
@ -30,6 +37,12 @@ class MatrixTimelineItemMapper(
|
|||
val asEvent = it.asEvent()
|
||||
if (asEvent != null) {
|
||||
val eventTimelineItem = eventTimelineItemMapper.map(asEvent)
|
||||
|
||||
|
||||
if (eventTimelineItem.hasNotLoadedInReplyTo() && eventTimelineItem.eventId != null) {
|
||||
fetchDetailsForEvent(eventTimelineItem.eventId!!)
|
||||
}
|
||||
|
||||
return MatrixTimelineItem.Event(eventTimelineItem)
|
||||
}
|
||||
val asVirtual = it.asVirtual()
|
||||
|
|
@ -39,4 +52,13 @@ class MatrixTimelineItemMapper(
|
|||
}
|
||||
return MatrixTimelineItem.Other
|
||||
}
|
||||
|
||||
private fun fetchDetailsForEvent(eventId: EventId) = coroutineScope.launch {
|
||||
runCatching {
|
||||
room.fetchDetailsForEvent(eventId.value)
|
||||
}.onFailure {
|
||||
Timber.e(it)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,6 +63,8 @@ class RustMatrixTimeline(
|
|||
)
|
||||
|
||||
private val timelineItemFactory = MatrixTimelineItemMapper(
|
||||
room = innerRoom,
|
||||
coroutineScope = coroutineScope,
|
||||
virtualTimelineItemMapper = VirtualTimelineItemMapper(),
|
||||
eventTimelineItemMapper = EventTimelineItemMapper(
|
||||
contentMapper = TimelineEventContentMapper(
|
||||
|
|
|
|||
|
|
@ -17,11 +17,13 @@
|
|||
package io.element.android.libraries.matrix.impl.timeline.item.event
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.AudioMessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EmoteMessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.FileMessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.FormattedBody
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.ImageMessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.MessageFormat
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessageType
|
||||
|
|
@ -31,6 +33,8 @@ import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageT
|
|||
import io.element.android.libraries.matrix.impl.media.map
|
||||
import org.matrix.rustcomponents.sdk.Message
|
||||
import org.matrix.rustcomponents.sdk.MessageType
|
||||
import org.matrix.rustcomponents.sdk.ProfileDetails
|
||||
import org.matrix.rustcomponents.sdk.RepliedToEventDetails
|
||||
import org.matrix.rustcomponents.sdk.use
|
||||
import org.matrix.rustcomponents.sdk.FormattedBody as RustFormattedBody
|
||||
import org.matrix.rustcomponents.sdk.MessageFormat as RustMessageFormat
|
||||
|
|
@ -66,9 +70,26 @@ class EventMessageMapper {
|
|||
}
|
||||
}
|
||||
}
|
||||
val inReplyToId = it.inReplyTo()?.eventId?.let(::EventId)
|
||||
val inReplyToEvent: InReplyTo? = (it.inReplyTo()?.event)?.use { details ->
|
||||
when (details) {
|
||||
is RepliedToEventDetails.Ready -> {
|
||||
val senderProfile = details.senderProfile as? ProfileDetails.Ready
|
||||
InReplyTo.Ready(
|
||||
eventId = inReplyToId!!,
|
||||
content = map(details.message),
|
||||
senderId = UserId(details.sender),
|
||||
senderDisplayName = senderProfile?.displayName,
|
||||
senderAvatarUrl = senderProfile?.avatarUrl,
|
||||
)
|
||||
}
|
||||
is RepliedToEventDetails.Error -> InReplyTo.Error
|
||||
is RepliedToEventDetails.Pending, is RepliedToEventDetails.Unavailable -> InReplyTo.NotLoaded(inReplyToId!!)
|
||||
}
|
||||
}
|
||||
MessageContent(
|
||||
body = it.body(),
|
||||
inReplyTo = it.inReplyTo()?.eventId?.let(::EventId),
|
||||
inReplyTo = inReplyToEvent,
|
||||
isEdited = it.isEdited(),
|
||||
type = type
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue