Timeline: extract highlightedEventId on viewState instead of individual timeline item
This commit is contained in:
parent
87cd42f4b5
commit
1344e0febd
6 changed files with 40 additions and 26 deletions
|
|
@ -27,7 +27,6 @@ class MessageTimelineItemStateFactory(
|
||||||
) {
|
) {
|
||||||
suspend fun create(
|
suspend fun create(
|
||||||
timelineItems: List<MatrixTimelineItem>,
|
timelineItems: List<MatrixTimelineItem>,
|
||||||
highlightedEventId: String? = null,
|
|
||||||
): List<MessagesTimelineItemState> =
|
): List<MessagesTimelineItemState> =
|
||||||
withContext(dispatcher) {
|
withContext(dispatcher) {
|
||||||
val messagesTimelineItemState = ArrayList<MessagesTimelineItemState>()
|
val messagesTimelineItemState = ArrayList<MessagesTimelineItemState>()
|
||||||
|
|
@ -39,7 +38,6 @@ class MessageTimelineItemStateFactory(
|
||||||
currentTimelineItem,
|
currentTimelineItem,
|
||||||
index,
|
index,
|
||||||
timelineItems,
|
timelineItems,
|
||||||
highlightedEventId
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is MatrixTimelineItem.Virtual -> MessagesTimelineItemState.Virtual(
|
is MatrixTimelineItem.Virtual -> MessagesTimelineItemState.Virtual(
|
||||||
|
|
@ -56,7 +54,6 @@ class MessageTimelineItemStateFactory(
|
||||||
currentTimelineItem: MatrixTimelineItem.Event,
|
currentTimelineItem: MatrixTimelineItem.Event,
|
||||||
index: Int,
|
index: Int,
|
||||||
timelineItems: List<MatrixTimelineItem>,
|
timelineItems: List<MatrixTimelineItem>,
|
||||||
highlightedEventId: String?,
|
|
||||||
): MessagesTimelineItemState.MessageEvent {
|
): MessagesTimelineItemState.MessageEvent {
|
||||||
val currentSender = currentTimelineItem.event.sender()
|
val currentSender = currentTimelineItem.event.sender()
|
||||||
val groupPosition =
|
val groupPosition =
|
||||||
|
|
@ -76,7 +73,6 @@ class MessageTimelineItemStateFactory(
|
||||||
senderAvatar = senderAvatarData,
|
senderAvatar = senderAvatarData,
|
||||||
content = currentTimelineItem.computeContent(),
|
content = currentTimelineItem.computeContent(),
|
||||||
isMine = currentTimelineItem.event.isOwn(),
|
isMine = currentTimelineItem.event.isOwn(),
|
||||||
isHighlighted = currentTimelineItem.event.eventId().orEmpty() == highlightedEventId,
|
|
||||||
groupPosition = groupPosition,
|
groupPosition = groupPosition,
|
||||||
reactionsState = currentTimelineItem.computeReactionsState()
|
reactionsState = currentTimelineItem.computeReactionsState()
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -53,10 +53,10 @@ private val COMPOSER_HEIGHT = 112.dp
|
||||||
@Composable
|
@Composable
|
||||||
fun MessagesScreen(
|
fun MessagesScreen(
|
||||||
roomId: String,
|
roomId: String,
|
||||||
onBackPressed: () -> Unit
|
onBackPressed: () -> Unit,
|
||||||
|
viewModel: MessagesViewModel = mavericksViewModel(argsFactory = { roomId }),
|
||||||
|
composerViewModel: MessageComposerViewModel = mavericksViewModel(argsFactory = { roomId })
|
||||||
) {
|
) {
|
||||||
val viewModel: MessagesViewModel = mavericksViewModel(argsFactory = { roomId })
|
|
||||||
val composerViewModel: MessageComposerViewModel = mavericksViewModel(argsFactory = { roomId })
|
|
||||||
|
|
||||||
fun onSendMessage(textMessage: String) {
|
fun onSendMessage(textMessage: String) {
|
||||||
viewModel.sendMessage(textMessage)
|
viewModel.sendMessage(textMessage)
|
||||||
|
|
@ -74,11 +74,13 @@ fun MessagesScreen(
|
||||||
val hasMoreToLoad by viewModel.collectAsState(MessagesViewState::hasMoreToLoad)
|
val hasMoreToLoad by viewModel.collectAsState(MessagesViewState::hasMoreToLoad)
|
||||||
val snackBarContent by viewModel.collectAsState(MessagesViewState::snackbarContent)
|
val snackBarContent by viewModel.collectAsState(MessagesViewState::snackbarContent)
|
||||||
val composerMode by viewModel.collectAsState(MessagesViewState::composerMode)
|
val composerMode by viewModel.collectAsState(MessagesViewState::composerMode)
|
||||||
|
val highlightedEventId by viewModel.collectAsState(MessagesViewState::highlightedEventId)
|
||||||
val composerFullScreen by composerViewModel.collectAsState(MessageComposerViewState::isFullScreen)
|
val composerFullScreen by composerViewModel.collectAsState(MessageComposerViewState::isFullScreen)
|
||||||
val composerCanSendMessage by composerViewModel.collectAsState(MessageComposerViewState::isSendButtonVisible)
|
val composerCanSendMessage by composerViewModel.collectAsState(MessageComposerViewState::isSendButtonVisible)
|
||||||
val composerText by composerViewModel.collectAsState(MessageComposerViewState::text)
|
val composerText by composerViewModel.collectAsState(MessageComposerViewState::text)
|
||||||
val snackbarHostState = remember { SnackbarHostState() }
|
val snackbarHostState = remember { SnackbarHostState() }
|
||||||
MessagesScreenContent(
|
MessagesScreenContent(
|
||||||
|
|
||||||
roomTitle = roomTitle,
|
roomTitle = roomTitle,
|
||||||
roomAvatar = roomAvatar,
|
roomAvatar = roomAvatar,
|
||||||
timelineItems = timelineItems().orEmpty(),
|
timelineItems = timelineItems().orEmpty(),
|
||||||
|
|
@ -90,6 +92,7 @@ fun MessagesScreen(
|
||||||
onComposerFullScreenChange = composerViewModel::onComposerFullScreenChange,
|
onComposerFullScreenChange = composerViewModel::onComposerFullScreenChange,
|
||||||
onComposerTextChange = composerViewModel::updateText,
|
onComposerTextChange = composerViewModel::updateText,
|
||||||
composerMode = composerMode,
|
composerMode = composerMode,
|
||||||
|
highlightedEventId = highlightedEventId,
|
||||||
onCloseSpecialMode = viewModel::setNormalMode,
|
onCloseSpecialMode = viewModel::setNormalMode,
|
||||||
composerCanSendMessage = composerCanSendMessage,
|
composerCanSendMessage = composerCanSendMessage,
|
||||||
composerText = composerText,
|
composerText = composerText,
|
||||||
|
|
@ -148,6 +151,7 @@ fun MessagesScreenContent(
|
||||||
onComposerFullScreenChange: () -> Unit,
|
onComposerFullScreenChange: () -> Unit,
|
||||||
onComposerTextChange: (CharSequence) -> Unit,
|
onComposerTextChange: (CharSequence) -> Unit,
|
||||||
composerMode: MessageComposerMode,
|
composerMode: MessageComposerMode,
|
||||||
|
highlightedEventId: String?,
|
||||||
onCloseSpecialMode: () -> Unit,
|
onCloseSpecialMode: () -> Unit,
|
||||||
composerCanSendMessage: Boolean,
|
composerCanSendMessage: Boolean,
|
||||||
composerText: StableCharSequence?,
|
composerText: StableCharSequence?,
|
||||||
|
|
@ -171,6 +175,7 @@ fun MessagesScreenContent(
|
||||||
onSendMessage = onSendMessage,
|
onSendMessage = onSendMessage,
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
onLongClick = onLongClick,
|
onLongClick = onLongClick,
|
||||||
|
highlightedEventId = highlightedEventId,
|
||||||
composerMode = composerMode,
|
composerMode = composerMode,
|
||||||
onCloseSpecialMode = onCloseSpecialMode,
|
onCloseSpecialMode = onCloseSpecialMode,
|
||||||
composerFullScreen = composerFullScreen,
|
composerFullScreen = composerFullScreen,
|
||||||
|
|
@ -193,6 +198,7 @@ fun MessagesContent(
|
||||||
onClick: (MessagesTimelineItemState.MessageEvent) -> Unit,
|
onClick: (MessagesTimelineItemState.MessageEvent) -> Unit,
|
||||||
onLongClick: (MessagesTimelineItemState.MessageEvent) -> Unit,
|
onLongClick: (MessagesTimelineItemState.MessageEvent) -> Unit,
|
||||||
composerMode: MessageComposerMode,
|
composerMode: MessageComposerMode,
|
||||||
|
highlightedEventId: String?,
|
||||||
onCloseSpecialMode: () -> Unit,
|
onCloseSpecialMode: () -> Unit,
|
||||||
composerFullScreen: Boolean,
|
composerFullScreen: Boolean,
|
||||||
onComposerFullScreenChange: () -> Unit,
|
onComposerFullScreenChange: () -> Unit,
|
||||||
|
|
@ -211,6 +217,7 @@ fun MessagesContent(
|
||||||
TimelineItems(
|
TimelineItems(
|
||||||
lazyListState = lazyListState,
|
lazyListState = lazyListState,
|
||||||
timelineItems = timelineItems,
|
timelineItems = timelineItems,
|
||||||
|
highlightedEventId = highlightedEventId,
|
||||||
hasMoreToLoad = hasMoreToLoad,
|
hasMoreToLoad = hasMoreToLoad,
|
||||||
onReachedLoadMore = onReachedLoadMore,
|
onReachedLoadMore = onReachedLoadMore,
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
|
|
@ -279,6 +286,7 @@ fun MessagesTopAppBar(
|
||||||
fun TimelineItems(
|
fun TimelineItems(
|
||||||
lazyListState: LazyListState,
|
lazyListState: LazyListState,
|
||||||
timelineItems: List<MessagesTimelineItemState>,
|
timelineItems: List<MessagesTimelineItemState>,
|
||||||
|
highlightedEventId: String?,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
hasMoreToLoad: Boolean = false,
|
hasMoreToLoad: Boolean = false,
|
||||||
onClick: (MessagesTimelineItemState.MessageEvent) -> Unit = {},
|
onClick: (MessagesTimelineItemState.MessageEvent) -> Unit = {},
|
||||||
|
|
@ -300,6 +308,7 @@ fun TimelineItems(
|
||||||
) { timelineItem ->
|
) { timelineItem ->
|
||||||
TimelineItemRow(
|
TimelineItemRow(
|
||||||
timelineItem = timelineItem,
|
timelineItem = timelineItem,
|
||||||
|
isHighlighted = timelineItem.key() == highlightedEventId,
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
onLongClick = onLongClick
|
onLongClick = onLongClick
|
||||||
)
|
)
|
||||||
|
|
@ -337,6 +346,7 @@ private fun MessagesTimelineItemState.contentType(): Int {
|
||||||
@Composable
|
@Composable
|
||||||
fun TimelineItemRow(
|
fun TimelineItemRow(
|
||||||
timelineItem: MessagesTimelineItemState,
|
timelineItem: MessagesTimelineItemState,
|
||||||
|
isHighlighted: Boolean,
|
||||||
onClick: (MessagesTimelineItemState.MessageEvent) -> Unit,
|
onClick: (MessagesTimelineItemState.MessageEvent) -> Unit,
|
||||||
onLongClick: (MessagesTimelineItemState.MessageEvent) -> Unit,
|
onLongClick: (MessagesTimelineItemState.MessageEvent) -> Unit,
|
||||||
) {
|
) {
|
||||||
|
|
@ -344,6 +354,7 @@ fun TimelineItemRow(
|
||||||
is MessagesTimelineItemState.Virtual -> return
|
is MessagesTimelineItemState.Virtual -> return
|
||||||
is MessagesTimelineItemState.MessageEvent -> MessageEventRow(
|
is MessagesTimelineItemState.MessageEvent -> MessageEventRow(
|
||||||
messageEvent = timelineItem,
|
messageEvent = timelineItem,
|
||||||
|
isHighlighted = isHighlighted,
|
||||||
onClick = { onClick(timelineItem) },
|
onClick = { onClick(timelineItem) },
|
||||||
onLongClick = { onLongClick(timelineItem) }
|
onLongClick = { onLongClick(timelineItem) }
|
||||||
)
|
)
|
||||||
|
|
@ -353,6 +364,7 @@ fun TimelineItemRow(
|
||||||
@Composable
|
@Composable
|
||||||
fun MessageEventRow(
|
fun MessageEventRow(
|
||||||
messageEvent: MessagesTimelineItemState.MessageEvent,
|
messageEvent: MessagesTimelineItemState.MessageEvent,
|
||||||
|
isHighlighted: Boolean,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
onLongClick: () -> Unit,
|
onLongClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
|
|
@ -385,7 +397,7 @@ fun MessageEventRow(
|
||||||
groupPosition = messageEvent.groupPosition,
|
groupPosition = messageEvent.groupPosition,
|
||||||
isMine = messageEvent.isMine,
|
isMine = messageEvent.isMine,
|
||||||
interactionSource = interactionSource,
|
interactionSource = interactionSource,
|
||||||
isHighlighted = messageEvent.isHighlighted,
|
isHighlighted = isHighlighted,
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
onLongClick = onLongClick,
|
onLongClick = onLongClick,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -584,6 +596,7 @@ fun TimelineItemsPreview(
|
||||||
groupPosition = MessagesItemGroupPosition.Last
|
groupPosition = MessagesItemGroupPosition.Last
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
highlightedEventId = null,
|
||||||
hasMoreToLoad = true,
|
hasMoreToLoad = true,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,9 @@ import io.element.android.x.matrix.room.MatrixRoom
|
||||||
import io.element.android.x.matrix.timeline.MatrixTimeline
|
import io.element.android.x.matrix.timeline.MatrixTimeline
|
||||||
import io.element.android.x.textcomposer.MessageComposerMode
|
import io.element.android.x.textcomposer.MessageComposerMode
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
|
|
@ -127,7 +129,8 @@ class MessagesViewModel(
|
||||||
private fun setComposerMode(mode: MessageComposerMode) {
|
private fun setComposerMode(mode: MessageComposerMode) {
|
||||||
setState {
|
setState {
|
||||||
copy(
|
copy(
|
||||||
composerMode = mode
|
composerMode = mode,
|
||||||
|
highlightedEventId = mode.relatedEventId
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -189,20 +192,9 @@ class MessagesViewModel(
|
||||||
}
|
}
|
||||||
}.launchIn(viewModelScope)
|
}.launchIn(viewModelScope)
|
||||||
|
|
||||||
combine(
|
timeline
|
||||||
timeline.timelineItems(),
|
.timelineItems()
|
||||||
stateFlow.map {
|
.map(messageTimelineItemStateFactory::create)
|
||||||
when (it.composerMode) {
|
|
||||||
is MessageComposerMode.Normal -> null
|
|
||||||
is MessageComposerMode.Edit -> it.composerMode.eventId
|
|
||||||
is MessageComposerMode.Quote -> null
|
|
||||||
is MessageComposerMode.Reply -> it.composerMode.eventId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.distinctUntilChanged()
|
|
||||||
) { timelineItems, highlightedEventId ->
|
|
||||||
messageTimelineItemStateFactory.create(timelineItems, highlightedEventId)
|
|
||||||
}
|
|
||||||
.execute {
|
.execute {
|
||||||
copy(timelineItems = it)
|
copy(timelineItems = it)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ sealed interface MessagesTimelineItemState {
|
||||||
val content: MessagesTimelineItemContent,
|
val content: MessagesTimelineItemContent,
|
||||||
val sentTime: String = "",
|
val sentTime: String = "",
|
||||||
val isMine: Boolean = false,
|
val isMine: Boolean = false,
|
||||||
val isHighlighted: Boolean = false,
|
|
||||||
val groupPosition: MessagesItemGroupPosition = MessagesItemGroupPosition.None,
|
val groupPosition: MessagesItemGroupPosition = MessagesItemGroupPosition.None,
|
||||||
val reactionsState: MessagesItemReactionState
|
val reactionsState: MessagesItemReactionState
|
||||||
) : MessagesTimelineItemState {
|
) : MessagesTimelineItemState {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
package io.element.android.x.features.messages.model
|
package io.element.android.x.features.messages.model
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
import com.airbnb.mvrx.Async
|
import com.airbnb.mvrx.Async
|
||||||
import com.airbnb.mvrx.MavericksState
|
import com.airbnb.mvrx.MavericksState
|
||||||
import com.airbnb.mvrx.Uninitialized
|
import com.airbnb.mvrx.Uninitialized
|
||||||
import io.element.android.x.designsystem.components.avatar.AvatarData
|
import io.element.android.x.designsystem.components.avatar.AvatarData
|
||||||
import io.element.android.x.textcomposer.MessageComposerMode
|
import io.element.android.x.textcomposer.MessageComposerMode
|
||||||
|
|
||||||
|
@Stable
|
||||||
data class MessagesViewState(
|
data class MessagesViewState(
|
||||||
val roomId: String,
|
val roomId: String,
|
||||||
val roomName: String? = null,
|
val roomName: String? = null,
|
||||||
|
|
@ -14,7 +16,7 @@ data class MessagesViewState(
|
||||||
val hasMoreToLoad: Boolean = true,
|
val hasMoreToLoad: Boolean = true,
|
||||||
val itemActionsSheetState: Async<MessagesItemActionsSheetState> = Uninitialized,
|
val itemActionsSheetState: Async<MessagesItemActionsSheetState> = Uninitialized,
|
||||||
val snackbarContent: String? = null,
|
val snackbarContent: String? = null,
|
||||||
// TODO Highlight item in reply / edit in the timeline
|
val highlightedEventId: String? = null,
|
||||||
val composerMode: MessageComposerMode = MessageComposerMode.Normal(""),
|
val composerMode: MessageComposerMode = MessageComposerMode.Normal(""),
|
||||||
) : MavericksState {
|
) : MavericksState {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,4 +33,16 @@ sealed interface MessageComposerMode {
|
||||||
override val eventId: String,
|
override val eventId: String,
|
||||||
override val defaultContent: CharSequence
|
override val defaultContent: CharSequence
|
||||||
) : Special(eventId, defaultContent)
|
) : Special(eventId, defaultContent)
|
||||||
|
|
||||||
|
|
||||||
|
val relatedEventId: String?
|
||||||
|
get() = when (this) {
|
||||||
|
is Normal -> null
|
||||||
|
is Edit -> eventId
|
||||||
|
is Quote -> eventId
|
||||||
|
is Reply -> eventId
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue