[Rich text editor] Integrate rich text editor library (#1172)
* Integrate rich text editor * Also increase swapfile size in test CI Fixes issue where screenshot tests are terminated due to lack of CI resources. See https://github.com/actions/runner-images/discussions/7188#discussioncomment-6750749 --------- Co-authored-by: ElementBot <benoitm+elementbot@element.io>
This commit is contained in:
parent
f96ba8c183
commit
f214493c9d
62 changed files with 441 additions and 289 deletions
|
|
@ -79,11 +79,11 @@ interface MatrixRoom : Closeable {
|
|||
|
||||
suspend fun userAvatarUrl(userId: UserId): Result<String?>
|
||||
|
||||
suspend fun sendMessage(message: String): Result<Unit>
|
||||
suspend fun sendMessage(body: String, htmlBody: String): Result<Unit>
|
||||
|
||||
suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, message: String): Result<Unit>
|
||||
suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, body: String, htmlBody: String): Result<Unit>
|
||||
|
||||
suspend fun replyMessage(eventId: EventId, message: String): Result<Unit>
|
||||
suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String): Result<Unit>
|
||||
|
||||
suspend fun redactEvent(eventId: EventId, reason: String? = null): Result<Unit>
|
||||
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ import org.matrix.rustcomponents.sdk.RoomMember
|
|||
import org.matrix.rustcomponents.sdk.RoomSubscription
|
||||
import org.matrix.rustcomponents.sdk.SendAttachmentJoinHandle
|
||||
import org.matrix.rustcomponents.sdk.genTransactionId
|
||||
import org.matrix.rustcomponents.sdk.messageEventContentFromMarkdown
|
||||
import org.matrix.rustcomponents.sdk.messageEventContentFromHtml
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
|
||||
|
|
@ -227,31 +227,32 @@ class RustMatrixRoom(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun sendMessage(message: String): Result<Unit> = withContext(roomDispatcher) {
|
||||
override suspend fun sendMessage(body: String, htmlBody: String): Result<Unit> = withContext(roomDispatcher) {
|
||||
val transactionId = genTransactionId()
|
||||
messageEventContentFromMarkdown(message).use { content ->
|
||||
messageEventContentFromHtml(body, htmlBody).use { content ->
|
||||
runCatching {
|
||||
innerRoom.send(content, transactionId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, message: String): Result<Unit> = withContext(roomDispatcher) {
|
||||
if (originalEventId != null) {
|
||||
runCatching {
|
||||
innerRoom.edit(messageEventContentFromMarkdown(message), originalEventId.value, transactionId?.value)
|
||||
}
|
||||
} else {
|
||||
runCatching {
|
||||
transactionId?.let { cancelSend(it) }
|
||||
innerRoom.send(messageEventContentFromMarkdown(message), genTransactionId())
|
||||
override suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, body: String, htmlBody: String): Result<Unit> =
|
||||
withContext(roomDispatcher) {
|
||||
if (originalEventId != null) {
|
||||
runCatching {
|
||||
innerRoom.edit(messageEventContentFromHtml(body, htmlBody), originalEventId.value, transactionId?.value)
|
||||
}
|
||||
} else {
|
||||
runCatching {
|
||||
transactionId?.let { cancelSend(it) }
|
||||
innerRoom.send(messageEventContentFromHtml(body, htmlBody), genTransactionId())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun replyMessage(eventId: EventId, message: String): Result<Unit> = withContext(roomDispatcher) {
|
||||
override suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String): Result<Unit> = withContext(roomDispatcher) {
|
||||
runCatching {
|
||||
innerRoom.sendReply(messageEventContentFromMarkdown(message), eventId.value, genTransactionId())
|
||||
innerRoom.sendReply(messageEventContentFromHtml(body, htmlBody), eventId.value, genTransactionId())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ class FakeMatrixRoom(
|
|||
private var sendPollResponseResult = Result.success(Unit)
|
||||
private var endPollResult = Result.success(Unit)
|
||||
private var progressCallbackValues = emptyList<Pair<Long, Long>>()
|
||||
val editMessageCalls = mutableListOf<String>()
|
||||
val editMessageCalls = mutableListOf<Pair<String, String>>()
|
||||
|
||||
var sendMediaCount = 0
|
||||
private set
|
||||
|
|
@ -171,7 +171,7 @@ class FakeMatrixRoom(
|
|||
userAvatarUrlResult
|
||||
}
|
||||
|
||||
override suspend fun sendMessage(message: String): Result<Unit> = simulateLongTask {
|
||||
override suspend fun sendMessage(body: String, htmlBody: String) = simulateLongTask {
|
||||
Result.success(Unit)
|
||||
}
|
||||
|
||||
|
|
@ -200,16 +200,16 @@ class FakeMatrixRoom(
|
|||
return cancelSendResult
|
||||
}
|
||||
|
||||
override suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, message: String): Result<Unit> {
|
||||
editMessageCalls += message
|
||||
override suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, body: String, htmlBody: String): Result<Unit> {
|
||||
editMessageCalls += body to htmlBody
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
var replyMessageParameter: String? = null
|
||||
var replyMessageParameter: Pair<String, String>? = null
|
||||
private set
|
||||
|
||||
override suspend fun replyMessage(eventId: EventId, message: String): Result<Unit> {
|
||||
replyMessageParameter = message
|
||||
override suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String): Result<Unit> {
|
||||
replyMessageParameter = body to htmlBody
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ class DefaultPermissionsPresenter @AssistedInject constructor(
|
|||
showDialog = showDialog.value,
|
||||
permissionAlreadyAsked = isAlreadyAsked,
|
||||
permissionAlreadyDenied = isAlreadyDenied,
|
||||
eventSink = ::handleEvents
|
||||
eventSink = { handleEvents(it) }
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -31,5 +31,9 @@ dependencies {
|
|||
implementation(projects.libraries.matrix.api)
|
||||
implementation(projects.libraries.matrixui)
|
||||
implementation(projects.libraries.designsystem)
|
||||
|
||||
implementation(libs.matrix.richtexteditor)
|
||||
api(libs.matrix.richtexteditor.compose)
|
||||
|
||||
ksp(libs.showkase.processor)
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.textcomposer
|
||||
|
||||
data class Message(
|
||||
val html: String,
|
||||
val markdown: String,
|
||||
)
|
||||
|
|
@ -37,38 +37,25 @@ import androidx.compose.foundation.layout.size
|
|||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.onFocusEvent
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
|
@ -88,23 +75,23 @@ import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo
|
|||
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType
|
||||
import io.element.android.libraries.theme.ElementTheme
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.wysiwyg.compose.RichTextEditor
|
||||
import io.element.android.wysiwyg.compose.RichTextEditorDefaults
|
||||
import io.element.android.wysiwyg.compose.RichTextEditorState
|
||||
import kotlinx.coroutines.android.awaitFrame
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class)
|
||||
@Composable
|
||||
fun TextComposer(
|
||||
composerText: String?,
|
||||
state: RichTextEditorState,
|
||||
composerMode: MessageComposerMode,
|
||||
composerCanSendMessage: Boolean,
|
||||
canSendMessage: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
focusRequester: FocusRequester = FocusRequester(),
|
||||
onSendMessage: (String) -> Unit = {},
|
||||
onRequestFocus: () -> Unit = {},
|
||||
onSendMessage: (Message) -> Unit = {},
|
||||
onResetComposerMode: () -> Unit = {},
|
||||
onComposerTextChange: (String) -> Unit = {},
|
||||
onAddAttachment: () -> Unit = {},
|
||||
onFocusChanged: (Boolean) -> Unit = {},
|
||||
onError: (Throwable) -> Unit = {},
|
||||
) {
|
||||
val text = composerText.orEmpty()
|
||||
Row(
|
||||
modifier.padding(
|
||||
horizontal = 12.dp,
|
||||
|
|
@ -115,10 +102,9 @@ fun TextComposer(
|
|||
Spacer(modifier = Modifier.width(12.dp))
|
||||
val roundCornerSmall = 20.dp.applyScaleUp()
|
||||
val roundCornerLarge = 28.dp.applyScaleUp()
|
||||
var lineCount by remember { mutableIntStateOf(0) }
|
||||
|
||||
val roundedCornerSize = remember(lineCount, composerMode) {
|
||||
if (lineCount > 1 || composerMode is MessageComposerMode.Special) {
|
||||
val roundedCornerSize = remember(state.lineCount, composerMode) {
|
||||
if (state.lineCount > 1 || composerMode is MessageComposerMode.Special) {
|
||||
roundCornerSmall
|
||||
} else {
|
||||
roundCornerLarge
|
||||
|
|
@ -132,10 +118,15 @@ fun TextComposer(
|
|||
)
|
||||
val roundedCorners = RoundedCornerShape(roundedCornerSizeState.value)
|
||||
val minHeight = 42.dp.applyScaleUp()
|
||||
val bgColor = ElementTheme.colors.bgSubtleSecondary
|
||||
// Change border color depending on focus
|
||||
var hasFocus by remember { mutableStateOf(false) }
|
||||
val borderColor = if (hasFocus) ElementTheme.colors.borderDisabled else bgColor
|
||||
val colors = ElementTheme.colors
|
||||
val bgColor = colors.bgSubtleSecondary
|
||||
|
||||
val borderColor by remember(state.hasFocus, colors) {
|
||||
derivedStateOf {
|
||||
if (state.hasFocus) colors.borderDisabled else bgColor
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
|
|
@ -147,66 +138,56 @@ fun TextComposer(
|
|||
ComposerModeView(composerMode = composerMode, onResetComposerMode = onResetComposerMode)
|
||||
}
|
||||
val defaultTypography = ElementTheme.typography.fontBodyLgRegular
|
||||
|
||||
Box {
|
||||
BasicTextField(
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(min = minHeight)
|
||||
.focusRequester(focusRequester)
|
||||
.onFocusEvent {
|
||||
hasFocus = it.hasFocus
|
||||
onFocusChanged(it.hasFocus)
|
||||
},
|
||||
value = text,
|
||||
onValueChange = { onComposerTextChange(it) },
|
||||
onTextLayout = {
|
||||
lineCount = it.lineCount
|
||||
},
|
||||
keyboardOptions = KeyboardOptions(
|
||||
capitalization = KeyboardCapitalization.Sentences,
|
||||
),
|
||||
textStyle = defaultTypography.copy(color = MaterialTheme.colorScheme.primary),
|
||||
cursorBrush = SolidColor(ElementTheme.colors.iconAccentTertiary),
|
||||
decorationBox = { innerTextField ->
|
||||
TextFieldDefaults.DecorationBox(
|
||||
value = text,
|
||||
innerTextField = innerTextField,
|
||||
enabled = true,
|
||||
singleLine = false,
|
||||
visualTransformation = VisualTransformation.None,
|
||||
shape = roundedCorners,
|
||||
contentPadding = PaddingValues(
|
||||
top = 10.dp.applyScaleUp(),
|
||||
bottom = 10.dp.applyScaleUp(),
|
||||
.background(color = bgColor, shape = roundedCorners)
|
||||
.padding(
|
||||
PaddingValues(
|
||||
top = 4.dp.applyScaleUp(),
|
||||
bottom = 4.dp.applyScaleUp(),
|
||||
start = 12.dp.applyScaleUp(),
|
||||
end = 42.dp.applyScaleUp(),
|
||||
),
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
placeholder = {
|
||||
Text(stringResource(CommonStrings.common_message), style = defaultTypography)
|
||||
},
|
||||
colors = TextFieldDefaults.colors(
|
||||
unfocusedTextColor = MaterialTheme.colorScheme.secondary,
|
||||
focusedTextColor = MaterialTheme.colorScheme.primary,
|
||||
unfocusedPlaceholderColor = ElementTheme.colors.textDisabled,
|
||||
focusedPlaceholderColor = ElementTheme.colors.textDisabled,
|
||||
unfocusedIndicatorColor = Color.Transparent,
|
||||
focusedIndicatorColor = Color.Transparent,
|
||||
disabledIndicatorColor = Color.Transparent,
|
||||
errorIndicatorColor = Color.Transparent,
|
||||
unfocusedContainerColor = bgColor,
|
||||
focusedContainerColor = bgColor,
|
||||
errorContainerColor = bgColor,
|
||||
disabledContainerColor = bgColor,
|
||||
end = 42.dp.applyScaleUp()
|
||||
)
|
||||
),
|
||||
contentAlignment = Alignment.CenterStart,
|
||||
) {
|
||||
|
||||
// Placeholder
|
||||
if (state.messageHtml.isEmpty()) {
|
||||
Text(
|
||||
stringResource(CommonStrings.common_message),
|
||||
style = defaultTypography.copy(
|
||||
color = ElementTheme.colors.textDisabled,
|
||||
),
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
RichTextEditor(
|
||||
state = state,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
style = RichTextEditorDefaults.style(
|
||||
text = RichTextEditorDefaults.textStyle(
|
||||
color = if (state.hasFocus) {
|
||||
MaterialTheme.colorScheme.primary
|
||||
} else {
|
||||
MaterialTheme.colorScheme.secondary
|
||||
}
|
||||
),
|
||||
cursor = RichTextEditorDefaults.cursorStyle(
|
||||
color = ElementTheme.colors.iconAccentTertiary,
|
||||
)
|
||||
),
|
||||
onError = onError
|
||||
)
|
||||
}
|
||||
|
||||
SendButton(
|
||||
text = text,
|
||||
canSendMessage = composerCanSendMessage,
|
||||
onSendMessage = onSendMessage,
|
||||
canSendMessage = canSendMessage,
|
||||
onClick = { onSendMessage(Message(html = state.messageHtml, markdown = state.messageMarkdown)) },
|
||||
composerMode = composerMode,
|
||||
modifier = Modifier.padding(end = 6.dp.applyScaleUp(), bottom = 6.dp.applyScaleUp())
|
||||
)
|
||||
|
|
@ -218,7 +199,7 @@ fun TextComposer(
|
|||
val keyboard = LocalSoftwareKeyboardController.current
|
||||
LaunchedEffect(composerMode) {
|
||||
if (composerMode is MessageComposerMode.Special) {
|
||||
focusRequester.requestFocus()
|
||||
onRequestFocus()
|
||||
keyboard?.let {
|
||||
awaitFrame()
|
||||
it.show()
|
||||
|
|
@ -241,7 +222,7 @@ private fun ComposerModeView(
|
|||
ReplyToModeView(
|
||||
modifier = modifier.padding(8.dp),
|
||||
senderName = composerMode.senderName,
|
||||
text = composerMode.defaultContent.toString(),
|
||||
text = composerMode.defaultContent,
|
||||
attachmentThumbnailInfo = composerMode.attachmentThumbnailInfo,
|
||||
onResetComposerMode = onResetComposerMode,
|
||||
)
|
||||
|
|
@ -385,9 +366,8 @@ private fun AttachmentButton(
|
|||
|
||||
@Composable
|
||||
private fun BoxScope.SendButton(
|
||||
text: String,
|
||||
canSendMessage: Boolean,
|
||||
onSendMessage: (String) -> Unit,
|
||||
onClick: () -> Unit,
|
||||
composerMode: MessageComposerMode,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
|
@ -405,9 +385,8 @@ private fun BoxScope.SendButton(
|
|||
enabled = canSendMessage,
|
||||
interactionSource = interactionSource,
|
||||
indication = rememberRipple(bounded = false),
|
||||
onClick = {
|
||||
onSendMessage(text)
|
||||
}),
|
||||
onClick = onClick,
|
||||
),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
val iconId = when (composerMode) {
|
||||
|
|
@ -433,28 +412,37 @@ private fun BoxScope.SendButton(
|
|||
internal fun TextComposerSimplePreview() = ElementPreview {
|
||||
Column {
|
||||
TextComposer(
|
||||
RichTextEditorState("", fake = true).apply { requestFocus() },
|
||||
canSendMessage = false,
|
||||
onSendMessage = {},
|
||||
onComposerTextChange = {},
|
||||
composerMode = MessageComposerMode.Normal(""),
|
||||
onResetComposerMode = {},
|
||||
composerCanSendMessage = false,
|
||||
composerText = "",
|
||||
)
|
||||
TextComposer(
|
||||
RichTextEditorState("A message", fake = true).apply { requestFocus() },
|
||||
canSendMessage = true,
|
||||
onSendMessage = {},
|
||||
onComposerTextChange = {},
|
||||
composerMode = MessageComposerMode.Normal(""),
|
||||
onResetComposerMode = {},
|
||||
composerCanSendMessage = true,
|
||||
composerText = "A message",
|
||||
)
|
||||
TextComposer(
|
||||
RichTextEditorState(
|
||||
"A message\nWith several lines\nTo preview larger textfields and long lines with overflow",
|
||||
fake = true
|
||||
).apply {
|
||||
requestFocus()
|
||||
},
|
||||
canSendMessage = true,
|
||||
onSendMessage = {},
|
||||
composerMode = MessageComposerMode.Normal(""),
|
||||
onResetComposerMode = {},
|
||||
)
|
||||
TextComposer(
|
||||
RichTextEditorState("A message without focus", fake = true),
|
||||
canSendMessage = true,
|
||||
onSendMessage = {},
|
||||
onComposerTextChange = {},
|
||||
composerMode = MessageComposerMode.Normal(""),
|
||||
onResetComposerMode = {},
|
||||
composerCanSendMessage = true,
|
||||
composerText = "A message\nWith several lines\nTo preview larger textfields and long lines with overflow",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -463,12 +451,11 @@ internal fun TextComposerSimplePreview() = ElementPreview {
|
|||
@Composable
|
||||
internal fun TextComposerEditPreview() = ElementPreview {
|
||||
TextComposer(
|
||||
RichTextEditorState("A message", fake = true).apply { requestFocus() },
|
||||
canSendMessage = true,
|
||||
onSendMessage = {},
|
||||
onComposerTextChange = {},
|
||||
composerMode = MessageComposerMode.Edit(EventId("$1234"), "Some text", TransactionId("1234")),
|
||||
onResetComposerMode = {},
|
||||
composerCanSendMessage = true,
|
||||
composerText = "A message",
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -477,8 +464,9 @@ internal fun TextComposerEditPreview() = ElementPreview {
|
|||
internal fun TextComposerReplyPreview() = ElementPreview {
|
||||
Column {
|
||||
TextComposer(
|
||||
RichTextEditorState("", fake = true),
|
||||
canSendMessage = false,
|
||||
onSendMessage = {},
|
||||
onComposerTextChange = {},
|
||||
composerMode = MessageComposerMode.Reply(
|
||||
senderName = "Alice",
|
||||
eventId = EventId("$1234"),
|
||||
|
|
@ -488,12 +476,11 @@ internal fun TextComposerReplyPreview() = ElementPreview {
|
|||
"To preview larger textfields and long lines with overflow"
|
||||
),
|
||||
onResetComposerMode = {},
|
||||
composerCanSendMessage = true,
|
||||
composerText = "A message",
|
||||
)
|
||||
TextComposer(
|
||||
RichTextEditorState("A message", fake = true),
|
||||
canSendMessage = true,
|
||||
onSendMessage = {},
|
||||
onComposerTextChange = {},
|
||||
composerMode = MessageComposerMode.Reply(
|
||||
senderName = "Alice",
|
||||
eventId = EventId("$1234"),
|
||||
|
|
@ -506,12 +493,11 @@ internal fun TextComposerReplyPreview() = ElementPreview {
|
|||
defaultContent = "image.jpg"
|
||||
),
|
||||
onResetComposerMode = {},
|
||||
composerCanSendMessage = true,
|
||||
composerText = "A message",
|
||||
)
|
||||
TextComposer(
|
||||
RichTextEditorState("A message", fake = true),
|
||||
canSendMessage = true,
|
||||
onSendMessage = {},
|
||||
onComposerTextChange = {},
|
||||
composerMode = MessageComposerMode.Reply(
|
||||
senderName = "Alice",
|
||||
eventId = EventId("$1234"),
|
||||
|
|
@ -524,12 +510,11 @@ internal fun TextComposerReplyPreview() = ElementPreview {
|
|||
defaultContent = "video.mp4"
|
||||
),
|
||||
onResetComposerMode = {},
|
||||
composerCanSendMessage = true,
|
||||
composerText = "A message",
|
||||
)
|
||||
TextComposer(
|
||||
RichTextEditorState("A message", fake = true),
|
||||
canSendMessage = true,
|
||||
onSendMessage = {},
|
||||
onComposerTextChange = {},
|
||||
composerMode = MessageComposerMode.Reply(
|
||||
senderName = "Alice",
|
||||
eventId = EventId("$1234"),
|
||||
|
|
@ -542,12 +527,11 @@ internal fun TextComposerReplyPreview() = ElementPreview {
|
|||
defaultContent = "logs.txt"
|
||||
),
|
||||
onResetComposerMode = {},
|
||||
composerCanSendMessage = true,
|
||||
composerText = "A message",
|
||||
)
|
||||
TextComposer(
|
||||
RichTextEditorState("A message", fake = true).apply { requestFocus() },
|
||||
canSendMessage = true,
|
||||
onSendMessage = {},
|
||||
onComposerTextChange = {},
|
||||
composerMode = MessageComposerMode.Reply(
|
||||
senderName = "Alice",
|
||||
eventId = EventId("$1234"),
|
||||
|
|
@ -560,8 +544,6 @@ internal fun TextComposerReplyPreview() = ElementPreview {
|
|||
defaultContent = "Shared location"
|
||||
),
|
||||
onResetComposerMode = {},
|
||||
composerCanSendMessage = true,
|
||||
composerText = "A message",
|
||||
)
|
||||
}
|
||||
}
|
||||
28
libraries/textcomposer/test/build.gradle.kts
Normal file
28
libraries/textcomposer/test/build.gradle.kts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id("io.element.android-compose-library")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "io.element.android.libraries.textcomposer.test"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(projects.libraries.textcomposer.impl)
|
||||
implementation(projects.tests.testutils)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue