Timeline: extract highlightedEventId on viewState instead of individual timeline item

This commit is contained in:
ganfra 2022-12-02 14:37:25 +01:00
parent 87cd42f4b5
commit 1344e0febd
6 changed files with 40 additions and 26 deletions

View file

@ -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()
) )

View file

@ -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,
) )
} }

View file

@ -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)
} }

View file

@ -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 {

View file

@ -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 {

View file

@ -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
}
} }