Merge develop into feature/fga/dagger_setup
This commit is contained in:
commit
4c88d8e3c2
214 changed files with 2662 additions and 1833 deletions
|
|
@ -1,13 +1,11 @@
|
|||
package io.element.android.x.features.messages
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
|
|
@ -21,4 +19,4 @@ class ExampleInstrumentedTest {
|
|||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
assertEquals("io.element.android.x.features.messages.test", appContext.packageName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<manifest>
|
||||
|
||||
</manifest>
|
||||
</manifest>
|
||||
|
|
|
|||
|
|
@ -9,12 +9,20 @@ import io.element.android.x.features.messages.model.AggregatedReaction
|
|||
import io.element.android.x.features.messages.model.MessagesItemGroupPosition
|
||||
import io.element.android.x.features.messages.model.MessagesItemReactionState
|
||||
import io.element.android.x.features.messages.model.MessagesTimelineItemState
|
||||
import io.element.android.x.features.messages.model.content.*
|
||||
import io.element.android.x.features.messages.model.content.MessagesTimelineItemContent
|
||||
import io.element.android.x.features.messages.model.content.MessagesTimelineItemEmoteContent
|
||||
import io.element.android.x.features.messages.model.content.MessagesTimelineItemEncryptedContent
|
||||
import io.element.android.x.features.messages.model.content.MessagesTimelineItemImageContent
|
||||
import io.element.android.x.features.messages.model.content.MessagesTimelineItemNoticeContent
|
||||
import io.element.android.x.features.messages.model.content.MessagesTimelineItemRedactedContent
|
||||
import io.element.android.x.features.messages.model.content.MessagesTimelineItemTextContent
|
||||
import io.element.android.x.features.messages.model.content.MessagesTimelineItemUnknownContent
|
||||
import io.element.android.x.features.messages.util.invalidateLast
|
||||
import io.element.android.x.matrix.MatrixClient
|
||||
import io.element.android.x.matrix.media.MediaResolver
|
||||
import io.element.android.x.matrix.room.MatrixRoom
|
||||
import io.element.android.x.matrix.timeline.MatrixTimelineItem
|
||||
import kotlin.system.measureTimeMillis
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
|
@ -28,7 +36,6 @@ import org.matrix.rustcomponents.sdk.FormattedBody
|
|||
import org.matrix.rustcomponents.sdk.MessageFormat
|
||||
import org.matrix.rustcomponents.sdk.MessageType
|
||||
import timber.log.Timber
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
class MessageTimelineItemStateFactory(
|
||||
private val client: MatrixClient,
|
||||
|
|
@ -83,7 +90,6 @@ class MessageTimelineItemStateFactory(
|
|||
timelineItemStates.emit(newTimelineItemStates)
|
||||
}
|
||||
|
||||
|
||||
private fun calculateAndApplyDiff(newTimelineItems: List<MatrixTimelineItem>) {
|
||||
val timeToDiff = measureTimeMillis {
|
||||
val diffCallback =
|
||||
|
|
@ -192,7 +198,6 @@ class MessageTimelineItemStateFactory(
|
|||
htmlDocument = messageType.content.formatted?.toHtmlDocument()
|
||||
)
|
||||
else -> MessagesTimelineItemUnknownContent
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -232,5 +237,4 @@ class MessageTimelineItemStateFactory(
|
|||
.resolve(url, kind = MediaResolver.Kind.Thumbnail(size.value))
|
||||
return AvatarData(name, model, size)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,26 @@
|
|||
|
||||
package io.element.android.x.features.messages
|
||||
|
||||
import Avatar
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.statusBars
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.items
|
||||
|
|
@ -20,8 +37,24 @@ import androidx.compose.material.icons.Icons
|
|||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.ArrowDownward
|
||||
import androidx.compose.material.rememberModalBottomSheetState
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Alignment.Companion.End
|
||||
import androidx.compose.ui.Alignment.Companion.Start
|
||||
|
|
@ -41,18 +74,40 @@ import com.airbnb.mvrx.compose.mavericksViewModel
|
|||
import io.element.android.x.core.compose.LogCompositions
|
||||
import io.element.android.x.core.compose.PairCombinedPreviewParameter
|
||||
import io.element.android.x.core.data.StableCharSequence
|
||||
import io.element.android.x.designsystem.components.avatar.Avatar
|
||||
import io.element.android.x.designsystem.components.avatar.AvatarData
|
||||
import io.element.android.x.features.messages.components.*
|
||||
import io.element.android.x.features.messages.model.*
|
||||
import io.element.android.x.features.messages.model.content.*
|
||||
import io.element.android.x.features.messages.components.MessageEventBubble
|
||||
import io.element.android.x.features.messages.components.MessagesReactionsView
|
||||
import io.element.android.x.features.messages.components.MessagesTimelineItemEncryptedView
|
||||
import io.element.android.x.features.messages.components.MessagesTimelineItemImageView
|
||||
import io.element.android.x.features.messages.components.MessagesTimelineItemRedactedView
|
||||
import io.element.android.x.features.messages.components.MessagesTimelineItemTextView
|
||||
import io.element.android.x.features.messages.components.MessagesTimelineItemUnknownView
|
||||
import io.element.android.x.features.messages.components.TimelineItemActionsScreen
|
||||
import io.element.android.x.features.messages.model.AggregatedReaction
|
||||
import io.element.android.x.features.messages.model.MessagesItemGroupPosition
|
||||
import io.element.android.x.features.messages.model.MessagesItemGroupPositionProvider
|
||||
import io.element.android.x.features.messages.model.MessagesItemReactionState
|
||||
import io.element.android.x.features.messages.model.MessagesTimelineItemState
|
||||
import io.element.android.x.features.messages.model.MessagesViewState
|
||||
import io.element.android.x.features.messages.model.content.MessagesTimelineItemContent
|
||||
import io.element.android.x.features.messages.model.content.MessagesTimelineItemContentProvider
|
||||
import io.element.android.x.features.messages.model.content.MessagesTimelineItemEncryptedContent
|
||||
import io.element.android.x.features.messages.model.content.MessagesTimelineItemImageContent
|
||||
import io.element.android.x.features.messages.model.content.MessagesTimelineItemRedactedContent
|
||||
import io.element.android.x.features.messages.model.content.MessagesTimelineItemTextBasedContent
|
||||
import io.element.android.x.features.messages.model.content.MessagesTimelineItemUnknownContent
|
||||
import io.element.android.x.features.messages.textcomposer.MessageComposerViewModel
|
||||
import io.element.android.x.features.messages.textcomposer.MessageComposerViewState
|
||||
import io.element.android.x.textcomposer.MessageComposerMode
|
||||
import io.element.android.x.textcomposer.TextComposer
|
||||
import java.lang.Math.random
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import java.lang.Math.random
|
||||
|
||||
@Composable
|
||||
fun MessagesScreen(
|
||||
|
|
@ -61,7 +116,6 @@ fun MessagesScreen(
|
|||
viewModel: MessagesViewModel = mavericksViewModel(argsFactory = { roomId }),
|
||||
composerViewModel: MessageComposerViewModel = mavericksViewModel(argsFactory = { roomId })
|
||||
) {
|
||||
|
||||
fun onSendMessage(textMessage: String) {
|
||||
viewModel.sendMessage(textMessage)
|
||||
composerViewModel.updateText("")
|
||||
|
|
@ -88,7 +142,7 @@ fun MessagesScreen(
|
|||
MessagesScreenContent(
|
||||
roomTitle = roomTitle,
|
||||
roomAvatar = roomAvatar,
|
||||
timelineItems = timelineItems().orEmpty(),
|
||||
timelineItems = timelineItems().orEmpty().toImmutableList(),
|
||||
hasMoreToLoad = hasMoreToLoad,
|
||||
onReachedLoadMore = viewModel::loadMore,
|
||||
onBackPressed = onBackPressed,
|
||||
|
|
@ -130,7 +184,7 @@ fun MessagesScreen(
|
|||
fun MessagesScreenContent(
|
||||
roomTitle: String?,
|
||||
roomAvatar: AvatarData?,
|
||||
timelineItems: List<MessagesTimelineItemState>,
|
||||
timelineItems: ImmutableList<MessagesTimelineItemState>,
|
||||
hasMoreToLoad: Boolean,
|
||||
onReachedLoadMore: () -> Unit,
|
||||
onBackPressed: () -> Unit,
|
||||
|
|
@ -146,9 +200,11 @@ fun MessagesScreenContent(
|
|||
composerCanSendMessage: Boolean,
|
||||
composerText: StableCharSequence?,
|
||||
snackbarHostState: SnackbarHostState,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
LogCompositions(tag = "MessagesScreen", msg = "Content")
|
||||
Scaffold(
|
||||
modifier = modifier,
|
||||
contentWindowInsets = WindowInsets.statusBars,
|
||||
topBar = {
|
||||
MessagesTopAppBar(
|
||||
|
|
@ -187,7 +243,7 @@ fun MessagesScreenContent(
|
|||
|
||||
@Composable
|
||||
fun MessagesContent(
|
||||
timelineItems: List<MessagesTimelineItemState>,
|
||||
timelineItems: ImmutableList<MessagesTimelineItemState>,
|
||||
hasMoreToLoad: Boolean,
|
||||
onReachedLoadMore: () -> Unit,
|
||||
onSendMessage: (String) -> Unit,
|
||||
|
|
@ -203,7 +259,6 @@ fun MessagesContent(
|
|||
composerText: StableCharSequence?,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
|
||||
val lazyListState = rememberLazyListState()
|
||||
Column(
|
||||
modifier = modifier
|
||||
|
|
@ -249,9 +304,11 @@ fun MessagesContent(
|
|||
fun MessagesTopAppBar(
|
||||
roomTitle: String?,
|
||||
roomAvatar: AvatarData?,
|
||||
onBackPressed: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
onBackPressed: () -> Unit = {},
|
||||
) {
|
||||
TopAppBar(
|
||||
modifier = modifier,
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onBackPressed) {
|
||||
Icon(
|
||||
|
|
@ -282,7 +339,7 @@ fun MessagesTopAppBar(
|
|||
@Composable
|
||||
fun TimelineItems(
|
||||
lazyListState: LazyListState,
|
||||
timelineItems: List<MessagesTimelineItemState>,
|
||||
timelineItems: ImmutableList<MessagesTimelineItemState>,
|
||||
highlightedEventId: String?,
|
||||
modifier: Modifier = Modifier,
|
||||
hasMoreToLoad: Boolean = false,
|
||||
|
|
@ -322,7 +379,6 @@ fun TimelineItems(
|
|||
onLoadMore = onReachedLoadMore
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun MessagesTimelineItemState.key(): String {
|
||||
|
|
@ -339,7 +395,6 @@ private fun MessagesTimelineItemState.contentType(): Int {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun TimelineItemRow(
|
||||
timelineItem: MessagesTimelineItemState,
|
||||
|
|
@ -426,7 +481,6 @@ fun MessageEventRow(
|
|||
content = messageEvent.content,
|
||||
modifier = contentModifier
|
||||
)
|
||||
else -> TODO() /* compiler issue ? */
|
||||
}
|
||||
}
|
||||
MessagesReactionsView(
|
||||
|
|
@ -471,8 +525,8 @@ private fun MessageSenderInformation(
|
|||
@Composable
|
||||
internal fun BoxScope.MessagesScrollHelper(
|
||||
lazyListState: LazyListState,
|
||||
timelineItems: List<MessagesTimelineItemState>,
|
||||
onLoadMore: () -> Unit,
|
||||
timelineItems: ImmutableList<MessagesTimelineItemState>,
|
||||
onLoadMore: () -> Unit = {},
|
||||
) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val firstVisibleItemIndex by remember { derivedStateOf { lazyListState.firstVisibleItemIndex } }
|
||||
|
|
@ -526,7 +580,6 @@ internal fun BoxScope.MessagesScrollHelper(
|
|||
Icon(Icons.Default.ArrowDownward, "")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -543,7 +596,6 @@ internal fun MessagesLoadingMoreIndicator() {
|
|||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class MessagesItemGroupPositionToMessagesTimelineItemContentProvider :
|
||||
|
|
@ -551,6 +603,7 @@ class MessagesItemGroupPositionToMessagesTimelineItemContentProvider :
|
|||
MessagesItemGroupPositionProvider() to MessagesTimelineItemContentProvider()
|
||||
)
|
||||
|
||||
@Suppress("PreviewPublic")
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun TimelineItemsPreview(
|
||||
|
|
@ -559,7 +612,7 @@ fun TimelineItemsPreview(
|
|||
) {
|
||||
TimelineItems(
|
||||
lazyListState = LazyListState(),
|
||||
timelineItems = listOf(
|
||||
timelineItems = persistentListOf(
|
||||
// 3 items (First Middle Last) with isMine = false
|
||||
createMessageEvent(
|
||||
isMine = false,
|
||||
|
|
|
|||
|
|
@ -218,4 +218,4 @@ class MessagesViewModel @AssistedInject constructor(
|
|||
timeline.callback = null
|
||||
timeline.dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,13 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.x.designsystem.*
|
||||
import io.element.android.x.designsystem.LocalIsDarkTheme
|
||||
import io.element.android.x.designsystem.MessageHighlightDark
|
||||
import io.element.android.x.designsystem.MessageHighlightLight
|
||||
import io.element.android.x.designsystem.SystemGrey5Dark
|
||||
import io.element.android.x.designsystem.SystemGrey5Light
|
||||
import io.element.android.x.designsystem.SystemGrey6Dark
|
||||
import io.element.android.x.designsystem.SystemGrey6Light
|
||||
import io.element.android.x.features.messages.model.MessagesItemGroupPosition
|
||||
|
||||
private val BUBBLE_RADIUS = 16.dp
|
||||
|
|
@ -26,9 +32,9 @@ fun MessageEventBubble(
|
|||
interactionSource: MutableInteractionSource,
|
||||
isHighlighted: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
onClick: () -> Unit,
|
||||
onLongClick: () -> Unit,
|
||||
content: @Composable () -> Unit,
|
||||
onClick: () -> Unit = {},
|
||||
onLongClick: () -> Unit = {},
|
||||
content: @Composable () -> Unit = {},
|
||||
) {
|
||||
fun bubbleShape(): Shape {
|
||||
return when (groupPosition) {
|
||||
|
|
@ -102,4 +108,4 @@ fun MessageEventBubble(
|
|||
shape = bubbleShape,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ fun MessagesReactionsView(
|
|||
reactionsState: MessagesItemReactionState,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if(reactionsState.reactions.isEmpty()) return
|
||||
if (reactionsState.reactions.isEmpty()) return
|
||||
FlowRow(
|
||||
modifier = modifier,
|
||||
mainAxisSpacing = 2.dp,
|
||||
|
|
@ -53,4 +53,4 @@ fun MessagesReactionButton(reaction: AggregatedReaction, modifier: Modifier = Mo
|
|||
Text(text = reaction.count, color = MaterialTheme.colorScheme.secondary, fontSize = 12.sp)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,14 +3,28 @@
|
|||
package io.element.android.x.features.messages.components
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.ListItem
|
||||
import androidx.compose.material.LocalContentColor
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.ModalBottomSheetLayout
|
||||
import androidx.compose.material.ModalBottomSheetState
|
||||
import androidx.compose.material.ModalBottomSheetValue
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.airbnb.mvrx.compose.collectAsState
|
||||
import io.element.android.x.designsystem.components.VectorIcon
|
||||
|
|
@ -21,7 +35,6 @@ import io.element.android.x.features.messages.model.MessagesTimelineItemState
|
|||
import io.element.android.x.features.messages.model.MessagesViewState
|
||||
import io.element.android.x.features.messages.model.content.MessagesTimelineItemTextBasedContent
|
||||
import io.element.android.x.features.messages.textcomposer.MessageComposerViewModel
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
|
@ -63,7 +76,6 @@ fun TimelineItemActionsScreen(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
ModalBottomSheetLayout(
|
||||
modifier = modifier,
|
||||
sheetState = modalBottomSheetState,
|
||||
|
|
@ -71,46 +83,47 @@ fun TimelineItemActionsScreen(
|
|||
SheetContent(
|
||||
actionsSheetState = itemActionsSheetState(),
|
||||
onActionClicked = ::onItemActionClicked,
|
||||
modifier = Modifier.navigationBarsPadding().imePadding()
|
||||
modifier = Modifier
|
||||
.navigationBarsPadding()
|
||||
.imePadding()
|
||||
)
|
||||
}
|
||||
) {}
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SheetContent(
|
||||
actionsSheetState: MessagesItemActionsSheetState?,
|
||||
onActionClicked: (MessagesItemAction, MessagesTimelineItemState.MessageEvent) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
modifier: Modifier = Modifier,
|
||||
onActionClicked: (MessagesItemAction, MessagesTimelineItemState.MessageEvent) -> Unit = { _, _ -> },
|
||||
) {
|
||||
if (actionsSheetState == null || actionsSheetState.actions.isEmpty()) {
|
||||
// Crashes if sheetContent size is zero
|
||||
Box(modifier = modifier.size(1.dp))
|
||||
return
|
||||
}
|
||||
LazyColumn(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
items(actionsSheetState.actions) {
|
||||
ListItem(
|
||||
modifier = Modifier.clickable {
|
||||
onActionClicked(it, actionsSheetState.targetItem)
|
||||
},
|
||||
text = {
|
||||
Text(
|
||||
text = it.title,
|
||||
color = if (it.destructive) MaterialTheme.colors.error else Color.Unspecified,
|
||||
)
|
||||
},
|
||||
icon = {
|
||||
VectorIcon(
|
||||
resourceId = it.icon,
|
||||
tint = if (it.destructive) MaterialTheme.colors.error else LocalContentColor.current,
|
||||
)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
LazyColumn(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
items(actionsSheetState.actions) {
|
||||
ListItem(
|
||||
modifier = Modifier.clickable {
|
||||
onActionClicked(it, actionsSheetState.targetItem)
|
||||
},
|
||||
text = {
|
||||
Text(
|
||||
text = it.title,
|
||||
color = if (it.destructive) MaterialTheme.colors.error else Color.Unspecified,
|
||||
)
|
||||
},
|
||||
icon = {
|
||||
VectorIcon(
|
||||
resourceId = it.icon,
|
||||
tint = if (it.destructive) MaterialTheme.colors.error else LocalContentColor.current,
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,4 +17,4 @@ fun MessagesTimelineItemEncryptedView(
|
|||
icon = Icons.Default.Warning,
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import androidx.compose.foundation.ExperimentalFoundationApi
|
|||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
|
|
@ -16,7 +15,6 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.graphics.painter.ColorPainter
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil.compose.AsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import io.element.android.x.features.messages.model.content.MessagesTimelineItemImageContent
|
||||
|
|
@ -26,9 +24,9 @@ fun MessagesTimelineItemImageView(
|
|||
content: MessagesTimelineItemImageContent,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val widthPercent = if(content.aspectRatio > 1f){
|
||||
val widthPercent = if (content.aspectRatio > 1f) {
|
||||
1f
|
||||
}else {
|
||||
} else {
|
||||
0.7f
|
||||
}
|
||||
Box(
|
||||
|
|
@ -37,7 +35,6 @@ fun MessagesTimelineItemImageView(
|
|||
.aspectRatio(content.aspectRatio),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
|
||||
var isLoading = rememberSaveable(content.imageMeta) { mutableStateOf(true) }
|
||||
val context = LocalContext.current
|
||||
val model = ImageRequest.Builder(context)
|
||||
|
|
@ -52,4 +49,4 @@ fun MessagesTimelineItemImageView(
|
|||
onSuccess = { isLoading.value = false },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,4 +40,4 @@ fun MessagesTimelineItemInformativeView(
|
|||
text = text
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,4 +17,4 @@ fun MessagesTimelineItemRedactedView(
|
|||
icon = Icons.Default.Delete,
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ fun MessagesTimelineItemTextView(
|
|||
content: MessagesTimelineItemTextBasedContent,
|
||||
interactionSource: MutableInteractionSource,
|
||||
modifier: Modifier = Modifier,
|
||||
onTextClicked: () -> Unit,
|
||||
onTextLongClicked: () -> Unit,
|
||||
onTextClicked: () -> Unit = {},
|
||||
onTextLongClicked: () -> Unit = {},
|
||||
) {
|
||||
val htmlDocument = content.htmlDocument
|
||||
if (htmlDocument != null) {
|
||||
|
|
@ -74,4 +74,4 @@ private fun String.linkify(
|
|||
end = end
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,4 +17,4 @@ fun MessagesTimelineItemUnknownView(
|
|||
icon = Icons.Default.Info,
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,17 +9,25 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.InlineTextContent
|
||||
import androidx.compose.foundation.text.appendInlineContent
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.material3.ColorScheme
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.drawBehind
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.*
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.google.accompanist.flowlayout.FlowRow
|
||||
|
|
@ -27,6 +35,7 @@ import io.element.android.x.designsystem.LinkColor
|
|||
import io.element.android.x.designsystem.components.ClickableLinkText
|
||||
import io.element.android.x.matrix.permalink.PermalinkData
|
||||
import io.element.android.x.matrix.permalink.PermalinkParser
|
||||
import kotlinx.collections.immutable.persistentMapOf
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import org.jsoup.nodes.Node
|
||||
|
|
@ -38,34 +47,33 @@ private const val chipId = "chip"
|
|||
fun HtmlDocument(
|
||||
document: Document,
|
||||
interactionSource: MutableInteractionSource,
|
||||
onTextClicked: () -> Unit,
|
||||
onTextLongClicked: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
onTextClicked: () -> Unit = {},
|
||||
onTextLongClicked: () -> Unit = {},
|
||||
) {
|
||||
HtmlBody(
|
||||
body = document.body(),
|
||||
interactionSource = interactionSource,
|
||||
modifier = modifier,
|
||||
onTextClicked = onTextClicked,
|
||||
onTextLongClicked = onTextLongClicked,
|
||||
interactionSource = interactionSource
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun HtmlBody(
|
||||
body: Element,
|
||||
modifier: Modifier = Modifier,
|
||||
onTextClicked: () -> Unit,
|
||||
onTextLongClicked: () -> Unit,
|
||||
interactionSource: MutableInteractionSource,
|
||||
modifier: Modifier = Modifier,
|
||||
onTextClicked: () -> Unit = {},
|
||||
onTextLongClicked: () -> Unit = {},
|
||||
) {
|
||||
|
||||
@Composable
|
||||
fun NodesFlowRode(
|
||||
nodes: Iterator<Node>,
|
||||
onTextClicked: () -> Unit,
|
||||
onTextLongClicked: () -> Unit,
|
||||
interactionSource: MutableInteractionSource,
|
||||
onTextClicked: () -> Unit = {},
|
||||
onTextLongClicked: () -> Unit = {},
|
||||
) = FlowRow(
|
||||
mainAxisSpacing = 2.dp,
|
||||
crossAxisSpacing = 8.dp,
|
||||
|
|
@ -106,9 +114,9 @@ private fun HtmlBody(
|
|||
while (nodesIterator.hasNext()) {
|
||||
NodesFlowRode(
|
||||
nodes = nodesIterator,
|
||||
interactionSource = interactionSource,
|
||||
onTextClicked = onTextClicked,
|
||||
onTextLongClicked = onTextLongClicked,
|
||||
interactionSource = interactionSource
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -125,10 +133,10 @@ private fun Element.isInline(): Boolean {
|
|||
@Composable
|
||||
private fun HtmlBlock(
|
||||
element: Element,
|
||||
modifier: Modifier = Modifier,
|
||||
onTextClicked: () -> Unit,
|
||||
onTextLongClicked: () -> Unit,
|
||||
interactionSource: MutableInteractionSource,
|
||||
modifier: Modifier = Modifier,
|
||||
onTextClicked: () -> Unit = {},
|
||||
onTextLongClicked: () -> Unit = {},
|
||||
) {
|
||||
val blockModifier = modifier
|
||||
.padding(top = 4.dp)
|
||||
|
|
@ -183,10 +191,10 @@ private fun HtmlBlock(
|
|||
@Composable
|
||||
private fun HtmlInline(
|
||||
element: Element,
|
||||
modifier: Modifier = Modifier,
|
||||
onTextClicked: () -> Unit,
|
||||
onTextLongClicked: () -> Unit,
|
||||
interactionSource: MutableInteractionSource,
|
||||
modifier: Modifier = Modifier,
|
||||
onTextClicked: () -> Unit = {},
|
||||
onTextLongClicked: () -> Unit = {},
|
||||
) {
|
||||
Box(modifier) {
|
||||
val styledText = buildAnnotatedString {
|
||||
|
|
@ -202,7 +210,10 @@ private fun HtmlInline(
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun HtmlPreformatted(pre: Element, modifier: Modifier = Modifier) {
|
||||
private fun HtmlPreformatted(
|
||||
pre: Element,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val isCode = pre.firstElementChild()?.normalName() == "code"
|
||||
val backgroundColor =
|
||||
if (isCode) MaterialTheme.colorScheme.codeBackground() else Color.Unspecified
|
||||
|
|
@ -221,9 +232,10 @@ private fun HtmlPreformatted(pre: Element, modifier: Modifier = Modifier) {
|
|||
@Composable
|
||||
private fun HtmlParagraph(
|
||||
paragraph: Element,
|
||||
modifier: Modifier = Modifier,
|
||||
onTextClicked: () -> Unit, onTextLongClicked: () -> Unit,
|
||||
interactionSource: MutableInteractionSource,
|
||||
modifier: Modifier = Modifier,
|
||||
onTextClicked: () -> Unit = {},
|
||||
onTextLongClicked: () -> Unit = {},
|
||||
) {
|
||||
Box(modifier) {
|
||||
val styledText = buildAnnotatedString {
|
||||
|
|
@ -239,9 +251,10 @@ private fun HtmlParagraph(
|
|||
@Composable
|
||||
private fun HtmlBlockquote(
|
||||
blockquote: Element,
|
||||
modifier: Modifier = Modifier,
|
||||
onTextClicked: () -> Unit, onTextLongClicked: () -> Unit,
|
||||
interactionSource: MutableInteractionSource,
|
||||
modifier: Modifier = Modifier,
|
||||
onTextClicked: () -> Unit = {},
|
||||
onTextLongClicked: () -> Unit = {},
|
||||
) {
|
||||
val color = MaterialTheme.colorScheme.onBackground
|
||||
Box(
|
||||
|
|
@ -268,13 +281,13 @@ private fun HtmlBlockquote(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
private fun HtmlHeading(
|
||||
heading: Element,
|
||||
modifier: Modifier = Modifier,
|
||||
onTextClicked: () -> Unit, onTextLongClicked: () -> Unit,
|
||||
interactionSource: MutableInteractionSource,
|
||||
modifier: Modifier = Modifier,
|
||||
onTextClicked: () -> Unit = {},
|
||||
onTextLongClicked: () -> Unit = {},
|
||||
) {
|
||||
val style = when (heading.normalName()) {
|
||||
"h1" -> MaterialTheme.typography.headlineLarge.copy(fontSize = 30.sp)
|
||||
|
|
@ -304,9 +317,10 @@ private fun HtmlHeading(
|
|||
@Composable
|
||||
private fun HtmlMxReply(
|
||||
mxReply: Element,
|
||||
modifier: Modifier = Modifier,
|
||||
onTextClicked: () -> Unit, onTextLongClicked: () -> Unit,
|
||||
interactionSource: MutableInteractionSource,
|
||||
modifier: Modifier = Modifier,
|
||||
onTextClicked: () -> Unit = {},
|
||||
onTextLongClicked: () -> Unit = {},
|
||||
) {
|
||||
val blockquote = mxReply.childNodes().firstOrNull() ?: return
|
||||
val shape = RoundedCornerShape(12.dp)
|
||||
|
|
@ -356,9 +370,10 @@ private fun HtmlMxReply(
|
|||
@Composable
|
||||
private fun HtmlOrderedList(
|
||||
orderedList: Element,
|
||||
modifier: Modifier = Modifier,
|
||||
onTextClicked: () -> Unit, onTextLongClicked: () -> Unit,
|
||||
interactionSource: MutableInteractionSource,
|
||||
modifier: Modifier = Modifier,
|
||||
onTextClicked: () -> Unit = {},
|
||||
onTextLongClicked: () -> Unit = {},
|
||||
) {
|
||||
var number = 1
|
||||
val delimiter = "."
|
||||
|
|
@ -381,9 +396,10 @@ private fun HtmlOrderedList(
|
|||
@Composable
|
||||
private fun HtmlUnorderedList(
|
||||
unorderedList: Element,
|
||||
modifier: Modifier = Modifier,
|
||||
onTextClicked: () -> Unit, onTextLongClicked: () -> Unit,
|
||||
interactionSource: MutableInteractionSource,
|
||||
modifier: Modifier = Modifier,
|
||||
onTextClicked: () -> Unit = {},
|
||||
onTextLongClicked: () -> Unit = {},
|
||||
) {
|
||||
val marker = "・"
|
||||
HtmlListItems(
|
||||
|
|
@ -402,14 +418,14 @@ private fun HtmlUnorderedList(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
private fun HtmlListItems(
|
||||
list: Element,
|
||||
modifier: Modifier = Modifier,
|
||||
onTextClicked: () -> Unit, onTextLongClicked: () -> Unit,
|
||||
interactionSource: MutableInteractionSource,
|
||||
content: @Composable (node: TextNode) -> Unit
|
||||
modifier: Modifier = Modifier,
|
||||
onTextClicked: () -> Unit = {},
|
||||
onTextLongClicked: () -> Unit = {},
|
||||
content: @Composable (node: TextNode) -> Unit = {}
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
for (node in list.children()) {
|
||||
|
|
@ -420,13 +436,12 @@ private fun HtmlListItems(
|
|||
}
|
||||
is Element -> HtmlBlock(
|
||||
element = innerNode,
|
||||
modifier = modifier.padding(start = 4.dp),
|
||||
modifier = Modifier.padding(start = 4.dp),
|
||||
onTextClicked = onTextClicked, onTextLongClicked = onTextLongClicked,
|
||||
interactionSource = interactionSource
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -439,7 +454,6 @@ private fun AnnotatedString.Builder.appendInlineChildrenElements(
|
|||
childNodes: List<Node>,
|
||||
colors: ColorScheme
|
||||
) {
|
||||
|
||||
for (node in childNodes) {
|
||||
when (node) {
|
||||
is TextNode -> {
|
||||
|
|
@ -452,7 +466,6 @@ private fun AnnotatedString.Builder.appendInlineChildrenElements(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private fun AnnotatedString.Builder.appendInlineElement(element: Element, colors: ColorScheme) {
|
||||
when (element.normalName()) {
|
||||
"br" -> {
|
||||
|
|
@ -490,7 +503,6 @@ private fun AnnotatedString.Builder.appendInlineElement(element: Element, colors
|
|||
appendInlineChildrenElements(element.childNodes(), colors)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun AnnotatedString.Builder.appendLink(link: Element) {
|
||||
|
|
@ -521,13 +533,13 @@ private fun AnnotatedString.Builder.appendLink(link: Element) {
|
|||
@Composable
|
||||
private fun HtmlText(
|
||||
text: AnnotatedString,
|
||||
interactionSource: MutableInteractionSource,
|
||||
modifier: Modifier = Modifier,
|
||||
style: TextStyle = LocalTextStyle.current,
|
||||
onClick: () -> Unit,
|
||||
onLongClick: () -> Unit,
|
||||
interactionSource: MutableInteractionSource,
|
||||
onClick: () -> Unit = {},
|
||||
onLongClick: () -> Unit = {},
|
||||
) {
|
||||
val inlineContentMap = emptyMap<String, InlineTextContent>()
|
||||
val inlineContentMap = persistentMapOf<String, InlineTextContent>()
|
||||
ClickableLinkText(
|
||||
text = text,
|
||||
linkAnnotationTag = "URL",
|
||||
|
|
@ -539,4 +551,3 @@ private fun HtmlText(
|
|||
onLongClick = onLongClick
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,5 +37,4 @@ internal class CacheInvalidator(private val itemStatesCache: MutableList<Message
|
|||
itemStatesCache.removeAt(position)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,5 +31,4 @@ internal class MatrixTimelineItemsDiffCallback(
|
|||
val newItem = newList.getOrNull(newItemPosition)
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,4 +15,4 @@ sealed class MessagesItemAction(
|
|||
object Redact : MessagesItemAction("Redact", VectorIcons.Delete, destructive = true)
|
||||
object Reply : MessagesItemAction("Reply", VectorIcons.Reply)
|
||||
object Edit : MessagesItemAction("Edit", VectorIcons.Edit)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ sealed interface MessagesItemGroupPosition {
|
|||
First, None -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal class MessagesItemGroupPositionProvider : PreviewParameterProvider<MessagesItemGroupPosition> {
|
||||
|
|
@ -22,4 +21,4 @@ internal class MessagesItemGroupPositionProvider : PreviewParameterProvider<Mess
|
|||
MessagesItemGroupPosition.Last,
|
||||
MessagesItemGroupPosition.None,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,10 @@ import androidx.compose.runtime.Stable
|
|||
data class MessagesItemReactionState(
|
||||
val reactions: List<AggregatedReaction>
|
||||
)
|
||||
|
||||
@Stable
|
||||
data class AggregatedReaction(
|
||||
val key: String,
|
||||
val count: String,
|
||||
val isHighlighted: Boolean = false
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -23,11 +23,5 @@ sealed interface MessagesTimelineItemState {
|
|||
val showSenderInformation = groupPosition.isNew() && !isMine
|
||||
|
||||
val safeSenderName: String = senderDisplayName ?: senderId
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@ package io.element.android.x.features.messages.model.content
|
|||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import org.matrix.rustcomponents.sdk.EncryptedMessage
|
||||
import org.matrix.rustcomponents.sdk.FormattedBody
|
||||
import org.matrix.rustcomponents.sdk.MessageFormat
|
||||
|
||||
sealed interface MessagesTimelineItemContent
|
||||
|
||||
|
|
@ -28,4 +26,4 @@ class MessagesTimelineItemContentProvider : PreviewParameterProvider<MessagesTim
|
|||
),
|
||||
MessagesTimelineItemUnknownContent,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,4 +5,4 @@ import org.jsoup.nodes.Document
|
|||
data class MessagesTimelineItemEmoteContent(
|
||||
override val body: String,
|
||||
override val htmlDocument: Document?
|
||||
) : MessagesTimelineItemTextBasedContent
|
||||
) : MessagesTimelineItemTextBasedContent
|
||||
|
|
|
|||
|
|
@ -4,4 +4,4 @@ import org.matrix.rustcomponents.sdk.EncryptedMessage
|
|||
|
||||
data class MessagesTimelineItemEncryptedContent(
|
||||
val encryptedMessage: EncryptedMessage
|
||||
) : MessagesTimelineItemContent
|
||||
) : MessagesTimelineItemContent
|
||||
|
|
|
|||
|
|
@ -7,4 +7,4 @@ data class MessagesTimelineItemImageContent(
|
|||
val imageMeta: MediaResolver.Meta,
|
||||
val blurhash: String?,
|
||||
val aspectRatio: Float
|
||||
) : MessagesTimelineItemContent
|
||||
) : MessagesTimelineItemContent
|
||||
|
|
|
|||
|
|
@ -5,4 +5,4 @@ import org.jsoup.nodes.Document
|
|||
data class MessagesTimelineItemNoticeContent(
|
||||
override val body: String,
|
||||
override val htmlDocument: Document?
|
||||
) : MessagesTimelineItemTextBasedContent
|
||||
) : MessagesTimelineItemTextBasedContent
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
package io.element.android.x.features.messages.model.content
|
||||
|
||||
object MessagesTimelineItemRedactedContent : MessagesTimelineItemContent
|
||||
object MessagesTimelineItemRedactedContent : MessagesTimelineItemContent
|
||||
|
|
|
|||
|
|
@ -5,4 +5,4 @@ import org.jsoup.nodes.Document
|
|||
sealed interface MessagesTimelineItemTextBasedContent : MessagesTimelineItemContent {
|
||||
val body: String
|
||||
val htmlDocument: Document?
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,4 +5,4 @@ import org.jsoup.nodes.Document
|
|||
data class MessagesTimelineItemTextContent(
|
||||
override val body: String,
|
||||
override val htmlDocument: Document?
|
||||
) : MessagesTimelineItemTextBasedContent
|
||||
) : MessagesTimelineItemTextBasedContent
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
package io.element.android.x.features.messages.model.content
|
||||
|
||||
object MessagesTimelineItemUnknownContent : MessagesTimelineItemContent
|
||||
object MessagesTimelineItemUnknownContent : MessagesTimelineItemContent
|
||||
|
|
|
|||
|
|
@ -35,4 +35,4 @@ class MessageComposerViewModel @AssistedInject constructor(
|
|||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,4 +16,4 @@ data class MessageComposerViewState(
|
|||
// val voiceBroadcastState: VoiceBroadcastState? = null,
|
||||
val text: StableCharSequence? = null,
|
||||
val isFullScreen: Boolean = false,
|
||||
) : MavericksState
|
||||
) : MavericksState
|
||||
|
|
|
|||
|
|
@ -5,4 +5,4 @@ internal inline fun <reified T> MutableList<T?>.invalidateLast() {
|
|||
if (indexOfLast > 0) {
|
||||
set(indexOfLast - 1, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
package io.element.android.x.features.messages
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
|
|
@ -14,4 +13,4 @@ class ExampleUnitTest {
|
|||
fun addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue