Merge pull request #1298 from vector-im/feature/fga/timeline_thread_decoration
Feature/fga/timeline thread decoration
This commit is contained in:
1
changelog.d/1236.feature
Normal file
1
changelog.d/1236.feature
Normal file
@@ -0,0 +1 @@
|
||||
Display a thread decorator in timeline so we know when a message is coming from a thread.
|
||||
@@ -209,7 +209,8 @@ class MessagesPresenter @AssistedInject constructor(
|
||||
TimelineItemAction.Copy -> handleCopyContents(targetEvent)
|
||||
TimelineItemAction.Redact -> handleActionRedact(targetEvent)
|
||||
TimelineItemAction.Edit -> handleActionEdit(targetEvent, composerState)
|
||||
TimelineItemAction.Reply -> handleActionReply(targetEvent, composerState)
|
||||
TimelineItemAction.Reply,
|
||||
TimelineItemAction.ReplyInThread -> handleActionReply(targetEvent, composerState)
|
||||
TimelineItemAction.Developer -> handleShowDebugInfoAction(targetEvent)
|
||||
TimelineItemAction.Forward -> handleForwardAction(targetEvent)
|
||||
TimelineItemAction.ReportContent -> handleReportAction(targetEvent)
|
||||
@@ -312,6 +313,7 @@ class MessagesPresenter @AssistedInject constructor(
|
||||
is TimelineItemUnknownContent -> null
|
||||
}
|
||||
val composerMode = MessageComposerMode.Reply(
|
||||
isThreaded = targetEvent.isThreaded,
|
||||
senderName = targetEvent.safeSenderName,
|
||||
eventId = targetEvent.eventId,
|
||||
attachmentThumbnailInfo = attachmentThumbnailInfo,
|
||||
|
||||
@@ -130,7 +130,11 @@ class ActionListPresenter @Inject constructor(
|
||||
if (timelineItem.isRemote) {
|
||||
// Can only reply or forward messages already uploaded to the server
|
||||
if (userCanSendMessage) {
|
||||
add(TimelineItemAction.Reply)
|
||||
if (timelineItem.isThreaded) {
|
||||
add(TimelineItemAction.ReplyInThread)
|
||||
} else {
|
||||
add(TimelineItemAction.Reply)
|
||||
}
|
||||
}
|
||||
add(TimelineItemAction.Forward)
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ sealed class TimelineItemAction(
|
||||
data object Copy : TimelineItemAction(CommonStrings.action_copy, VectorIcons.Copy)
|
||||
data object Redact : TimelineItemAction(CommonStrings.action_remove, VectorIcons.Delete, destructive = true)
|
||||
data object Reply : TimelineItemAction(CommonStrings.action_reply, VectorIcons.Reply)
|
||||
data object ReplyInThread : TimelineItemAction(CommonStrings.action_reply_in_thread, VectorIcons.Reply)
|
||||
data object Edit : TimelineItemAction(CommonStrings.action_edit, VectorIcons.Edit)
|
||||
data object Developer : TimelineItemAction(CommonStrings.action_view_source, VectorIcons.DeveloperMode)
|
||||
data object ReportContent : TimelineItemAction(CommonStrings.action_report_content, VectorIcons.ReportContent, destructive = true)
|
||||
|
||||
@@ -111,6 +111,7 @@ internal fun aTimelineItemEvent(
|
||||
groupPosition: TimelineItemGroupPosition = TimelineItemGroupPosition.None,
|
||||
sendState: LocalEventSendState = LocalEventSendState.Sent(eventId),
|
||||
inReplyTo: InReplyTo? = null,
|
||||
isThreaded: Boolean = false,
|
||||
debugInfo: TimelineItemDebugInfo = aTimelineItemDebugInfo(),
|
||||
timelineItemReactions: TimelineItemReactions = aTimelineItemReactions(),
|
||||
): TimelineItem.Event {
|
||||
@@ -129,6 +130,7 @@ internal fun aTimelineItemEvent(
|
||||
localSendState = sendState,
|
||||
inReplyTo = inReplyTo,
|
||||
debugInfo = debugInfo,
|
||||
isThreaded = isThreaded,
|
||||
origin = null
|
||||
)
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import androidx.compose.foundation.gestures.Orientation
|
||||
import androidx.compose.foundation.gestures.draggable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Arrangement.spacedBy
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
@@ -75,6 +76,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
|
||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemPollContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
|
||||
import io.element.android.libraries.designsystem.VectorIcons
|
||||
import io.element.android.libraries.designsystem.colors.AvatarColorsProvider
|
||||
import io.element.android.libraries.designsystem.components.EqualWidthColumn
|
||||
import io.element.android.libraries.designsystem.components.avatar.Avatar
|
||||
@@ -84,6 +86,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
||||
import io.element.android.libraries.designsystem.swipe.SwipeableActionsState
|
||||
import io.element.android.libraries.designsystem.swipe.rememberSwipeableActionsState
|
||||
import io.element.android.libraries.designsystem.text.toPx
|
||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
@@ -370,14 +373,6 @@ private fun MessageEventBubbleContent(
|
||||
onPollAnswerSelected: (pollStartId: EventId, answerId: String) -> Unit,
|
||||
@SuppressLint("ModifierParameter") bubbleModifier: Modifier = Modifier, // need to rename this modifier to distinguish it from the following ones
|
||||
) {
|
||||
val timestampPosition = when (event.content) {
|
||||
is TimelineItemImageContent,
|
||||
is TimelineItemVideoContent,
|
||||
is TimelineItemLocationContent -> TimestampPosition.Overlay
|
||||
is TimelineItemPollContent -> TimestampPosition.Below
|
||||
else -> TimestampPosition.Default
|
||||
}
|
||||
val replyToDetails = event.inReplyTo as? InReplyTo.Ready
|
||||
|
||||
// Long clicks are not not automatically propagated from a `clickable`
|
||||
// to its `combinedClickable` parent so we do it manually
|
||||
@@ -398,6 +393,24 @@ private fun MessageEventBubbleContent(
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ThreadDecoration(
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
horizontalArrangement = spacedBy(4.dp, Alignment.Start),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(resourceId = VectorIcons.ThreadDecoration, contentDescription = null, tint = ElementTheme.colors.iconSecondary)
|
||||
Text(
|
||||
text = stringResource(CommonStrings.common_thread),
|
||||
style = ElementTheme.typography.fontBodyXsRegular,
|
||||
color = ElementTheme.colors.textPrimary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ContentAndTimestampView(
|
||||
timestampPosition: TimestampPosition,
|
||||
@@ -450,47 +463,74 @@ private fun MessageEventBubbleContent(
|
||||
/** Groups the different components in a Column with some space between them. */
|
||||
@Composable
|
||||
fun CommonLayout(
|
||||
timestampPosition: TimestampPosition,
|
||||
showThreadDecoration: Boolean,
|
||||
inReplyToDetails: InReplyTo.Ready?,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
var modifierWithPadding: Modifier = Modifier
|
||||
var contentModifier: Modifier = Modifier
|
||||
EqualWidthColumn(modifier = modifier, spacing = 8.dp) {
|
||||
when {
|
||||
inReplyToDetails != null -> {
|
||||
val senderName = inReplyToDetails.senderDisplayName ?: inReplyToDetails.senderId.value
|
||||
val attachmentThumbnailInfo = attachmentThumbnailInfoForInReplyTo(inReplyToDetails)
|
||||
val text = textForInReplyTo(inReplyToDetails)
|
||||
ReplyToContent(
|
||||
senderName = senderName,
|
||||
text = text,
|
||||
attachmentThumbnailInfo = attachmentThumbnailInfo,
|
||||
modifier = Modifier
|
||||
.padding(top = 8.dp, start = 8.dp, end = 8.dp)
|
||||
.clip(RoundedCornerShape(6.dp))
|
||||
.clickable(enabled = true, onClick = inReplyToClick),
|
||||
)
|
||||
if (timestampPosition == TimestampPosition.Overlay) {
|
||||
modifierWithPadding = Modifier.padding(start = 8.dp, end = 8.dp, bottom = 8.dp)
|
||||
contentModifier = Modifier.clip(RoundedCornerShape(12.dp))
|
||||
} else {
|
||||
contentModifier = Modifier.padding(start = 12.dp, end = 12.dp, top = 0.dp, bottom = 8.dp)
|
||||
}
|
||||
}
|
||||
timestampPosition != TimestampPosition.Overlay -> {
|
||||
contentModifier = Modifier.padding(start = 12.dp, end = 12.dp, top = 8.dp, bottom = 8.dp)
|
||||
val modifierWithPadding: Modifier
|
||||
val contentModifier: Modifier
|
||||
when {
|
||||
inReplyToDetails != null -> {
|
||||
if (timestampPosition == TimestampPosition.Overlay) {
|
||||
modifierWithPadding = Modifier.padding(start = 8.dp, end = 8.dp, bottom = 8.dp)
|
||||
contentModifier = Modifier.clip(RoundedCornerShape(12.dp))
|
||||
} else {
|
||||
contentModifier = Modifier.padding(start = 12.dp, end = 12.dp, top = 0.dp, bottom = 8.dp)
|
||||
modifierWithPadding = Modifier
|
||||
}
|
||||
}
|
||||
timestampPosition != TimestampPosition.Overlay -> {
|
||||
modifierWithPadding = Modifier
|
||||
contentModifier = Modifier.padding(start = 12.dp, end = 12.dp, top = 8.dp, bottom = 8.dp)
|
||||
}
|
||||
else -> {
|
||||
modifierWithPadding = Modifier
|
||||
contentModifier = Modifier
|
||||
}
|
||||
}
|
||||
|
||||
EqualWidthColumn(modifier = modifier, spacing = 8.dp) {
|
||||
if (showThreadDecoration) {
|
||||
ThreadDecoration(modifier = Modifier.padding(top = 8.dp, start = 12.dp, end = 12.dp))
|
||||
}
|
||||
if (inReplyToDetails != null) {
|
||||
val senderName = inReplyToDetails.senderDisplayName ?: inReplyToDetails.senderId.value
|
||||
val attachmentThumbnailInfo = attachmentThumbnailInfoForInReplyTo(inReplyToDetails)
|
||||
val text = textForInReplyTo(inReplyToDetails)
|
||||
val topPadding = if (showThreadDecoration) 0.dp else 8.dp
|
||||
ReplyToContent(
|
||||
senderName = senderName,
|
||||
text = text,
|
||||
attachmentThumbnailInfo = attachmentThumbnailInfo,
|
||||
modifier = Modifier
|
||||
.padding(top = topPadding, start = 8.dp, end = 8.dp)
|
||||
.clip(RoundedCornerShape(6.dp))
|
||||
.clickable(enabled = true, onClick = inReplyToClick),
|
||||
)
|
||||
}
|
||||
ContentAndTimestampView(
|
||||
timestampPosition = timestampPosition,
|
||||
contentModifier = contentModifier,
|
||||
modifier = modifierWithPadding,
|
||||
contentModifier = contentModifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
CommonLayout(inReplyToDetails = replyToDetails, modifier = bubbleModifier)
|
||||
val timestampPosition = when (event.content) {
|
||||
is TimelineItemImageContent,
|
||||
is TimelineItemVideoContent,
|
||||
is TimelineItemLocationContent -> TimestampPosition.Overlay
|
||||
is TimelineItemPollContent -> TimestampPosition.Below
|
||||
else -> TimestampPosition.Default
|
||||
}
|
||||
val replyToDetails = event.inReplyTo as? InReplyTo.Ready
|
||||
CommonLayout(
|
||||
showThreadDecoration = event.isThreaded,
|
||||
timestampPosition = timestampPosition,
|
||||
inReplyToDetails = replyToDetails,
|
||||
modifier = bubbleModifier
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -694,6 +734,7 @@ private fun ContentToPreviewWithReply() {
|
||||
aspectRatio = 5f
|
||||
),
|
||||
inReplyTo = aInReplyToReady(replyContent),
|
||||
isThreaded = true,
|
||||
groupPosition = TimelineItemGroupPosition.Last,
|
||||
),
|
||||
isHighlighted = false,
|
||||
@@ -714,11 +755,11 @@ private fun ContentToPreviewWithReply() {
|
||||
}
|
||||
|
||||
private fun aInReplyToReady(
|
||||
replyContent: String
|
||||
replyContent: String,
|
||||
): InReplyTo.Ready {
|
||||
return InReplyTo.Ready(
|
||||
eventId = EventId("\$event"),
|
||||
content = MessageContent(replyContent, null, false, TextMessageType(replyContent, null)),
|
||||
content = MessageContent(replyContent, null, false, false, TextMessageType(replyContent, null)),
|
||||
senderId = UserId("@Sender:domain"),
|
||||
senderDisplayName = "Sender",
|
||||
senderAvatarUrl = null,
|
||||
|
||||
@@ -71,6 +71,7 @@ class TimelineItemEventFactory @Inject constructor(
|
||||
url = senderAvatarUrl,
|
||||
size = AvatarSize.TimelineSender
|
||||
)
|
||||
currentTimelineItem.event
|
||||
return TimelineItem.Event(
|
||||
id = currentTimelineItem.uniqueId.toString(),
|
||||
eventId = currentTimelineItem.eventId,
|
||||
@@ -85,6 +86,7 @@ class TimelineItemEventFactory @Inject constructor(
|
||||
reactionsState = currentTimelineItem.computeReactionsState(),
|
||||
localSendState = currentTimelineItem.event.localSendState,
|
||||
inReplyTo = currentTimelineItem.event.inReplyTo(),
|
||||
isThreaded = currentTimelineItem.event.isThreaded(),
|
||||
debugInfo = currentTimelineItem.event.debugInfo,
|
||||
origin = currentTimelineItem.event.origin,
|
||||
)
|
||||
|
||||
@@ -66,6 +66,7 @@ sealed interface TimelineItem {
|
||||
val reactionsState: TimelineItemReactions,
|
||||
val localSendState: LocalEventSendState?,
|
||||
val inReplyTo: InReplyTo?,
|
||||
val isThreaded: Boolean,
|
||||
val debugInfo: TimelineItemDebugInfo,
|
||||
val origin: TimelineItemEventOrigin?,
|
||||
) : TimelineItem {
|
||||
|
||||
@@ -37,6 +37,7 @@ internal fun aMessageEvent(
|
||||
isMine: Boolean = true,
|
||||
content: TimelineItemEventContent = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false),
|
||||
inReplyTo: InReplyTo? = null,
|
||||
isThreaded: Boolean = false,
|
||||
debugInfo: TimelineItemDebugInfo = aTimelineItemDebugInfo(),
|
||||
sendState: LocalEventSendState = LocalEventSendState.Sent(AN_EVENT_ID),
|
||||
) = TimelineItem.Event(
|
||||
@@ -52,5 +53,6 @@ internal fun aMessageEvent(
|
||||
localSendState = sendState,
|
||||
inReplyTo = inReplyTo,
|
||||
debugInfo = debugInfo,
|
||||
isThreaded = isThreaded,
|
||||
origin = null
|
||||
)
|
||||
|
||||
@@ -632,7 +632,7 @@ fun anEditMode(
|
||||
transactionId: TransactionId? = null,
|
||||
) = MessageComposerMode.Edit(eventId, message, transactionId)
|
||||
|
||||
fun aReplyMode() = MessageComposerMode.Reply(A_USER_NAME, null, AN_EVENT_ID, A_MESSAGE)
|
||||
fun aReplyMode() = MessageComposerMode.Reply(A_USER_NAME, null, false, AN_EVENT_ID, A_MESSAGE)
|
||||
fun aQuoteMode() = MessageComposerMode.Quote(AN_EVENT_ID, A_MESSAGE)
|
||||
|
||||
private fun String.toMessage() = Message(
|
||||
|
||||
@@ -44,6 +44,7 @@ class TimelineItemGrouperTest {
|
||||
reactionsState = aTimelineItemReactions(count = 0),
|
||||
localSendState = LocalEventSendState.Sent(AN_EVENT_ID),
|
||||
inReplyTo = null,
|
||||
isThreaded = false,
|
||||
debugInfo = aTimelineItemDebugInfo(),
|
||||
origin = null
|
||||
)
|
||||
|
||||
@@ -41,4 +41,5 @@ object VectorIcons {
|
||||
val Quote = R.drawable.ic_quote
|
||||
val Strikethrough = R.drawable.ic_strikethrough
|
||||
val Underline = R.drawable.ic_underline
|
||||
val ThreadDecoration = R.drawable.ic_thread_decoration
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="14dp"
|
||||
android:height="13dp"
|
||||
android:viewportWidth="14"
|
||||
android:viewportHeight="13">
|
||||
<path
|
||||
android:pathData="M3.667,4.667C3.478,4.667 3.319,4.603 3.192,4.475C3.064,4.347 3,4.189 3,4C3,3.811 3.064,3.653 3.192,3.525C3.319,3.397 3.478,3.333 3.667,3.333H10.333C10.522,3.333 10.681,3.397 10.808,3.525C10.936,3.653 11,3.811 11,4C11,4.189 10.936,4.347 10.808,4.475C10.681,4.603 10.522,4.667 10.333,4.667H3.667Z"
|
||||
android:fillColor="#656D77"/>
|
||||
<path
|
||||
android:pathData="M3.667,7.333C3.478,7.333 3.319,7.269 3.192,7.142C3.064,7.014 3,6.856 3,6.667C3,6.478 3.064,6.319 3.192,6.192C3.319,6.064 3.478,6 3.667,6H7.667C7.855,6 8.014,6.064 8.142,6.192C8.269,6.319 8.333,6.478 8.333,6.667C8.333,6.856 8.269,7.014 8.142,7.142C8.014,7.269 7.855,7.333 7.667,7.333H3.667Z"
|
||||
android:fillColor="#656D77"/>
|
||||
<path
|
||||
android:pathData="M1.471,12.195C1.051,12.615 0.333,12.318 0.333,11.724V1.333C0.333,0.597 0.93,0 1.667,0H12.333C13.07,0 13.667,0.597 13.667,1.333V9.333C13.667,10.07 13.07,10.667 12.333,10.667H3L1.471,12.195ZM3,9.333H12.333V1.333H1.667V10.114L2.057,9.724C2.307,9.474 2.646,9.333 3,9.333Z"
|
||||
android:fillColor="#656D77"/>
|
||||
</vector>
|
||||
@@ -153,7 +153,7 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
fun `Message contents`() {
|
||||
val body = "Shared body"
|
||||
fun createMessageContent(type: MessageType): MessageContent {
|
||||
return MessageContent(body, null, false, type)
|
||||
return MessageContent(body, null, false, false,type)
|
||||
}
|
||||
|
||||
val sharedContentMessagesTypes = arrayOf(
|
||||
|
||||
@@ -32,6 +32,7 @@ data class MessageContent(
|
||||
val body: String,
|
||||
val inReplyTo: InReplyTo?,
|
||||
val isEdited: Boolean,
|
||||
val isThreaded: Boolean,
|
||||
val type: MessageType?
|
||||
) : EventContent
|
||||
|
||||
|
||||
@@ -40,6 +40,11 @@ data class EventTimelineItem(
|
||||
fun inReplyTo(): InReplyTo? {
|
||||
return (content as? MessageContent)?.inReplyTo
|
||||
}
|
||||
|
||||
fun isThreaded(): Boolean {
|
||||
return (content as? MessageContent)?.isThreaded ?: false
|
||||
}
|
||||
|
||||
fun hasNotLoadedInReplyTo(): Boolean {
|
||||
val details = inReplyTo()
|
||||
return details is InReplyTo.NotLoaded
|
||||
|
||||
@@ -68,6 +68,7 @@ class EventMessageMapper {
|
||||
body = it.body(),
|
||||
inReplyTo = inReplyToEvent,
|
||||
isEdited = it.isEdited(),
|
||||
isThreaded = it.isThreaded(),
|
||||
type = type
|
||||
)
|
||||
}
|
||||
|
||||
@@ -147,6 +147,7 @@ fun aMessageContent(
|
||||
body: String = "body",
|
||||
inReplyTo: InReplyTo? = null,
|
||||
isEdited: Boolean = false,
|
||||
isThreaded: Boolean = false,
|
||||
messageType: MessageType = TextMessageType(
|
||||
body = body,
|
||||
formatted = null
|
||||
@@ -155,6 +156,7 @@ fun aMessageContent(
|
||||
body = body,
|
||||
inReplyTo = inReplyTo,
|
||||
isEdited = isEdited,
|
||||
isThreaded = isThreaded,
|
||||
type = messageType
|
||||
)
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ sealed interface MessageComposerMode : Parcelable {
|
||||
class Reply(
|
||||
val senderName: String,
|
||||
val attachmentThumbnailInfo: AttachmentThumbnailInfo?,
|
||||
val isThreaded: Boolean,
|
||||
override val eventId: EventId,
|
||||
override val defaultContent: String
|
||||
) : Special(eventId, defaultContent)
|
||||
@@ -60,5 +61,5 @@ sealed interface MessageComposerMode : Parcelable {
|
||||
get() = this is Reply
|
||||
|
||||
val inThread: Boolean
|
||||
get() = false // TODO
|
||||
get() = this is Reply && isThreaded
|
||||
}
|
||||
|
||||
@@ -185,9 +185,13 @@ fun TextComposer(
|
||||
if (composerMode is MessageComposerMode.Special) {
|
||||
ComposerModeView(composerMode = composerMode, onResetComposerMode = onResetComposerMode)
|
||||
}
|
||||
|
||||
TextInput(
|
||||
state = state,
|
||||
placeholder = if (composerMode.inThread) {
|
||||
stringResource(id = CommonStrings.action_reply_in_thread)
|
||||
} else {
|
||||
stringResource(id = CommonStrings.rich_text_editor_composer_placeholder)
|
||||
},
|
||||
roundedCorners = roundedCorners,
|
||||
bgColor = bgColor,
|
||||
onError = onError,
|
||||
@@ -239,6 +243,7 @@ fun TextComposer(
|
||||
@Composable
|
||||
private fun TextInput(
|
||||
state: RichTextEditorState,
|
||||
placeholder: String,
|
||||
roundedCorners: RoundedCornerShape,
|
||||
bgColor: Color,
|
||||
modifier: Modifier = Modifier,
|
||||
@@ -265,7 +270,7 @@ private fun TextInput(
|
||||
// Placeholder
|
||||
if (state.messageHtml.isEmpty()) {
|
||||
Text(
|
||||
stringResource(CommonStrings.common_message),
|
||||
placeholder,
|
||||
style = defaultTypography.copy(
|
||||
color = ElementTheme.colors.textDisabled,
|
||||
),
|
||||
@@ -689,6 +694,23 @@ internal fun TextComposerReplyPreview() = ElementPreview {
|
||||
canSendMessage = false,
|
||||
onSendMessage = {},
|
||||
composerMode = MessageComposerMode.Reply(
|
||||
isThreaded = false,
|
||||
senderName = "Alice",
|
||||
eventId = EventId("$1234"),
|
||||
attachmentThumbnailInfo = null,
|
||||
defaultContent = "A message\n" +
|
||||
"With several lines\n" +
|
||||
"To preview larger textfields and long lines with overflow"
|
||||
),
|
||||
onResetComposerMode = {},
|
||||
enableTextFormatting = true,
|
||||
)
|
||||
TextComposer(
|
||||
RichTextEditorState("", fake = true),
|
||||
canSendMessage = false,
|
||||
onSendMessage = {},
|
||||
composerMode = MessageComposerMode.Reply(
|
||||
isThreaded = true,
|
||||
senderName = "Alice",
|
||||
eventId = EventId("$1234"),
|
||||
attachmentThumbnailInfo = null,
|
||||
@@ -704,6 +726,7 @@ internal fun TextComposerReplyPreview() = ElementPreview {
|
||||
canSendMessage = true,
|
||||
onSendMessage = {},
|
||||
composerMode = MessageComposerMode.Reply(
|
||||
isThreaded = true,
|
||||
senderName = "Alice",
|
||||
eventId = EventId("$1234"),
|
||||
attachmentThumbnailInfo = AttachmentThumbnailInfo(
|
||||
@@ -722,6 +745,7 @@ internal fun TextComposerReplyPreview() = ElementPreview {
|
||||
canSendMessage = true,
|
||||
onSendMessage = {},
|
||||
composerMode = MessageComposerMode.Reply(
|
||||
isThreaded = false,
|
||||
senderName = "Alice",
|
||||
eventId = EventId("$1234"),
|
||||
attachmentThumbnailInfo = AttachmentThumbnailInfo(
|
||||
@@ -740,6 +764,7 @@ internal fun TextComposerReplyPreview() = ElementPreview {
|
||||
canSendMessage = true,
|
||||
onSendMessage = {},
|
||||
composerMode = MessageComposerMode.Reply(
|
||||
isThreaded = false,
|
||||
senderName = "Alice",
|
||||
eventId = EventId("$1234"),
|
||||
attachmentThumbnailInfo = AttachmentThumbnailInfo(
|
||||
@@ -758,6 +783,7 @@ internal fun TextComposerReplyPreview() = ElementPreview {
|
||||
canSendMessage = true,
|
||||
onSendMessage = {},
|
||||
composerMode = MessageComposerMode.Reply(
|
||||
isThreaded = false,
|
||||
senderName = "Alice",
|
||||
eventId = EventId("$1234"),
|
||||
attachmentThumbnailInfo = AttachmentThumbnailInfo(
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user