Merge pull request #1298 from vector-im/feature/fga/timeline_thread_decoration
Feature/fga/timeline thread decoration
This commit is contained in:
commit
239730702d
32 changed files with 178 additions and 69 deletions
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(
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f3ea303577f655368800debe9c40e1292dc08a20da7f2ea6ddddb87f8407b112
|
||||
size 10431
|
||||
oid sha256:6bf428927e9a3493284d9fa7ba307b51315ed52b317a60ac345e87ba70849d0f
|
||||
size 10523
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b646c06e55b50b64eb7b566fbc0bcafa7f7e348398f87152d008a47e7448f4e0
|
||||
size 10736
|
||||
oid sha256:6eced1d7173c2d0100351f5bb9cd14c649a826b2e355e426b9c6d4add90015d9
|
||||
size 10833
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b991a34a137d82ff4dedf48316a05ebbc008233984b11af70e64889a5fb5eda8
|
||||
size 127438
|
||||
oid sha256:2bf08003a076d8a206782888cc2fc3df297e53478cd4c0e0aa5c8a26069bb7fa
|
||||
size 128065
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:43b6514716d18382c8962520928d9d2ff6d0f665b2471e8a24e5d2e44a25c88c
|
||||
size 132295
|
||||
oid sha256:8f5f950fce40c10710eb7fe4b193b4623633acdedfcf9029d6e0920e0c8d43d6
|
||||
size 133043
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:fd991a67e7d6c1169e08e62cb74ed6fa7d12ad7f91d69bbfa778e0053efebfec
|
||||
size 52284
|
||||
oid sha256:c329165fa341a2130b43a448b8bba465f1e5f458136efa6da80f2cdeee9d6caa
|
||||
size 52369
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d421656ae8ad316e65f406c74dffa2ceb4e47b868b7f033f9cc4139e3e6be788
|
||||
size 53909
|
||||
oid sha256:2b4e5e9d920ea2a7733453e030bb365c9e2af7263de8fe216ab56088e0861fa5
|
||||
size 53997
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d0e4c8da669ee5383a7d0f828a3300b923ec38ab5e04bc388da0e83f1cf1ccf3
|
||||
size 38291
|
||||
oid sha256:1f065f63fb37fa9a5441cfaeca04e867e4e8f6f744dd02a83c459fc128ee353d
|
||||
size 38384
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:1f26aa7b13403cfaa864778a6ec5c26edde5932f3d887c7148d3a2b9f60f6e6a
|
||||
size 36392
|
||||
oid sha256:a5ded1cbc536c544b0cb188ba0a333a541157eaf1772cd0e06c7338307549adc
|
||||
size 36483
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e65e15c721a939ed700719cacbdee57e1ccb89d984e88bdb5ed772c4b583472a
|
||||
size 81494
|
||||
oid sha256:69e98a3521ae6545700e395bff211b06bb02095b01d9c254b3e0d2d0b8b88d26
|
||||
size 80484
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:49f6d3b7f93008640abf30a5a65ddca6845e16343ef97c3450faf9dd6d9b9cec
|
||||
size 78788
|
||||
oid sha256:27dee9eaae6736a128107c9fd93048a677287a40d24c334223243b3c55f1cf69
|
||||
size 77686
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:2a998d3db9e4454fe74f31028b1dc61e4d6c07c824192f5e75563234dedf0b26
|
||||
size 44108
|
||||
oid sha256:267f482ceddeadea4c3b580d6783fed1048015fa05a11e385dd547e60f72e1e8
|
||||
size 44206
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:97ab0a0b64ea7704a1557fc34b24d7caea34f9951948f5f5637f0bda596288dd
|
||||
size 41455
|
||||
oid sha256:9f5e7ab52469b406509964d71f12475df546973f1382944ce7f2e1a437e4f880
|
||||
size 41536
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue