From 7b197e6e8bb35ec0df3451dd79d1b47806adb2f4 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 13 Jan 2023 18:05:14 +0100 Subject: [PATCH] Finish migration of Messages screen --- .../java/io/element/android/x/di/AppModule.kt | 15 +++ .../x/features/messages/MessagesEvents.kt | 6 +- .../x/features/messages/MessagesNode.kt | 1 + .../x/features/messages/MessagesPresenter.kt | 21 ++-- .../x/features/messages/MessagesState.kt | 6 +- .../x/features/messages/MessagesView.kt | 63 ++++++---- .../messages/actionlist/ActionListEvents.kt | 4 +- .../actionlist/ActionListPresenter.kt | 15 +-- .../messages/actionlist/ActionListState.kt | 11 +- .../messages/actionlist/ActionListView.kt | 9 +- .../{ => model}/TimelineItemAction.kt | 4 +- .../textcomposer/MessageComposerPresenter.kt | 24 ++-- .../textcomposer/MessageComposerState.kt | 20 ++-- .../textcomposer/MessageComposerView.kt | 2 +- .../TimelineItemsFactory.kt} | 101 ++++++++-------- .../messages/timeline/TimelinePresenter.kt | 21 ++-- .../messages/timeline/TimelineState.kt | 12 +- .../messages/timeline/TimelineView.kt | 112 +++++++++--------- .../timeline/components/MessageEventBubble.kt | 2 +- ...edView.kt => TimelineItemEncryptedView.kt} | 8 +- ...mImageView.kt => TimelineItemImageView.kt} | 6 +- ...View.kt => TimelineItemInformativeView.kt} | 2 +- ...nsView.kt => TimelineItemReactionsView.kt} | 8 +- ...tedView.kt => TimelineItemRedactedView.kt} | 9 +- ...temTextView.kt => TimelineItemTextView.kt} | 6 +- ...nownView.kt => TimelineItemUnknownView.kt} | 8 +- .../{ => timeline}/diff/CacheInvalidator.kt | 10 +- .../diff/MatrixTimelineItemsDiffCallback.kt | 2 +- .../model/TimelineItem.kt} | 21 ++-- .../model/TimelineItemGroupPosition.kt} | 8 +- .../model/TimelineItemReactions.kt} | 12 +- .../model/content/TimelineItemContent.kt} | 18 +-- .../content/TimelineItemEmoteContent.kt} | 6 +- .../content/TimelineItemEncryptedContent.kt} | 6 +- .../content/TimelineItemImageContent.kt} | 6 +- .../content/TimelineItemNoticeContent.kt} | 6 +- .../content/TimelineItemRedactedContent.kt} | 4 +- .../content/TimelineItemTextBasedContent.kt} | 4 +- .../model/content/TimelineItemTextContent.kt} | 6 +- .../content/TimelineItemUnknownContent.kt} | 4 +- .../{ => timeline}/util/MutableListExt.kt | 2 +- .../io/element/android/x/matrix/Matrix.kt | 17 +-- .../android/x/matrix/room/MatrixRoom.kt | 21 ++-- .../x/matrix/timeline/MatrixTimeline.kt | 5 +- .../android/x/matrix/ui/MatrixItemHelper.kt | 3 +- libraries/textcomposer/build.gradle.kts | 1 + .../x/textcomposer/MessageComposerMode.kt | 11 +- 47 files changed, 354 insertions(+), 315 deletions(-) rename features/messages/src/main/java/io/element/android/x/features/messages/actionlist/{ => model}/TimelineItemAction.kt (92%) rename features/messages/src/main/java/io/element/android/x/features/messages/{MessageTimelineItemStateFactory.kt => timeline/TimelineItemsFactory.kt} (70%) rename features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/{MessagesTimelineItemEncryptedView.kt => TimelineItemEncryptedView.kt} (82%) rename features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/{MessagesTimelineItemImageView.kt => TimelineItemImageView.kt} (92%) rename features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/{MessagesTimelineItemInformativeView.kt => TimelineItemInformativeView.kt} (97%) rename features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/{MessagesReactionsView.kt => TimelineItemReactionsView.kt} (91%) rename features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/{MessagesTimelineItemRedactedView.kt => TimelineItemRedactedView.kt} (76%) rename features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/{MessagesTimelineItemTextView.kt => TimelineItemTextView.kt} (94%) rename features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/{MessagesTimelineItemUnknownView.kt => TimelineItemUnknownView.kt} (82%) rename features/messages/src/main/java/io/element/android/x/features/messages/{ => timeline}/diff/CacheInvalidator.kt (86%) rename features/messages/src/main/java/io/element/android/x/features/messages/{ => timeline}/diff/MatrixTimelineItemsDiffCallback.kt (96%) rename features/messages/src/main/java/io/element/android/x/features/messages/{model/MessagesTimelineItemState.kt => timeline/model/TimelineItem.kt} (70%) rename features/messages/src/main/java/io/element/android/x/features/messages/{model/MessagesItemGroupPosition.kt => timeline/model/TimelineItemGroupPosition.kt} (84%) rename features/messages/src/main/java/io/element/android/x/features/messages/{model/MessagesItemReactionState.kt => timeline/model/TimelineItemReactions.kt} (74%) rename features/messages/src/main/java/io/element/android/x/features/messages/{model/content/MessagesTimelineItemContent.kt => timeline/model/content/TimelineItemContent.kt} (74%) rename features/messages/src/main/java/io/element/android/x/features/messages/{model/content/MessagesTimelineItemTextContent.kt => timeline/model/content/TimelineItemEmoteContent.kt} (82%) rename features/messages/src/main/java/io/element/android/x/features/messages/{model/content/MessagesTimelineItemEncryptedContent.kt => timeline/model/content/TimelineItemEncryptedContent.kt} (83%) rename features/messages/src/main/java/io/element/android/x/features/messages/{model/content/MessagesTimelineItemImageContent.kt => timeline/model/content/TimelineItemImageContent.kt} (84%) rename features/messages/src/main/java/io/element/android/x/features/messages/{model/content/MessagesTimelineItemEmoteContent.kt => timeline/model/content/TimelineItemNoticeContent.kt} (82%) rename features/messages/src/main/java/io/element/android/x/features/messages/{model/content/MessagesTimelineItemUnknownContent.kt => timeline/model/content/TimelineItemRedactedContent.kt} (81%) rename features/messages/src/main/java/io/element/android/x/features/messages/{model/content/MessagesTimelineItemTextBasedContent.kt => timeline/model/content/TimelineItemTextBasedContent.kt} (82%) rename features/messages/src/main/java/io/element/android/x/features/messages/{model/content/MessagesTimelineItemNoticeContent.kt => timeline/model/content/TimelineItemTextContent.kt} (82%) rename features/messages/src/main/java/io/element/android/x/features/messages/{model/content/MessagesTimelineItemRedactedContent.kt => timeline/model/content/TimelineItemUnknownContent.kt} (81%) rename features/messages/src/main/java/io/element/android/x/features/messages/{ => timeline}/util/MutableListExt.kt (92%) diff --git a/app/src/main/java/io/element/android/x/di/AppModule.kt b/app/src/main/java/io/element/android/x/di/AppModule.kt index b8e703c775..55c331680c 100644 --- a/app/src/main/java/io/element/android/x/di/AppModule.kt +++ b/app/src/main/java/io/element/android/x/di/AppModule.kt @@ -19,10 +19,14 @@ package io.element.android.x.di import com.squareup.anvil.annotations.ContributesTo import dagger.Module import dagger.Provides +import io.element.android.x.core.coroutine.CoroutineDispatchers import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.MainScope +import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.plus +import java.util.concurrent.Executors @Module @ContributesTo(AppScope::class) @@ -33,4 +37,15 @@ object AppModule { fun providesAppCoroutineScope(): CoroutineScope { return MainScope() + CoroutineName("ElementX Scope") } + + @Provides + @SingleIn(AppScope::class) + fun providesCoroutineDispatchers(): CoroutineDispatchers { + return CoroutineDispatchers( + io = Dispatchers.IO, + computation = Dispatchers.Default, + main = Dispatchers.Main, + diffUpdateDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher() + ) + } } diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/MessagesEvents.kt b/features/messages/src/main/java/io/element/android/x/features/messages/MessagesEvents.kt index b70c896e9f..45d4049713 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/MessagesEvents.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/MessagesEvents.kt @@ -1,8 +1,8 @@ package io.element.android.x.features.messages -import io.element.android.x.features.messages.actionlist.TimelineItemAction -import io.element.android.x.features.messages.model.MessagesTimelineItemState +import io.element.android.x.features.messages.actionlist.model.TimelineItemAction +import io.element.android.x.features.messages.timeline.model.TimelineItem sealed interface MessagesEvents { - data class HandleAction(val action: TimelineItemAction, val messageEvent: MessagesTimelineItemState.MessageEvent) : MessagesEvents + data class HandleAction(val action: TimelineItemAction, val messageEvent: TimelineItem.MessageEvent) : MessagesEvents } diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/MessagesNode.kt b/features/messages/src/main/java/io/element/android/x/features/messages/MessagesNode.kt index d556eecf04..d02973f0dd 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/MessagesNode.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/MessagesNode.kt @@ -28,6 +28,7 @@ class MessagesNode @AssistedInject constructor( MessagesView( state = state, onBackPressed = this::navigateUp, + modifier = modifier ) } } diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/MessagesPresenter.kt b/features/messages/src/main/java/io/element/android/x/features/messages/MessagesPresenter.kt index d78123ef3d..679109a6e0 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/MessagesPresenter.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/MessagesPresenter.kt @@ -12,13 +12,14 @@ import io.element.android.x.architecture.Presenter import io.element.android.x.designsystem.components.avatar.AvatarData import io.element.android.x.designsystem.components.avatar.AvatarSize import io.element.android.x.features.messages.actionlist.ActionListPresenter -import io.element.android.x.features.messages.actionlist.TimelineItemAction -import io.element.android.x.features.messages.model.MessagesTimelineItemState -import io.element.android.x.features.messages.model.content.MessagesTimelineItemTextBasedContent +import io.element.android.x.features.messages.actionlist.model.TimelineItemAction import io.element.android.x.features.messages.textcomposer.MessageComposerEvents import io.element.android.x.features.messages.textcomposer.MessageComposerPresenter import io.element.android.x.features.messages.textcomposer.MessageComposerState +import io.element.android.x.features.messages.timeline.TimelineEvents import io.element.android.x.features.messages.timeline.TimelinePresenter +import io.element.android.x.features.messages.timeline.model.TimelineItem +import io.element.android.x.features.messages.timeline.model.content.TimelineItemTextBasedContent import io.element.android.x.matrix.MatrixClient import io.element.android.x.matrix.room.MatrixRoom import io.element.android.x.matrix.ui.MatrixItemHelper @@ -60,7 +61,9 @@ class MessagesPresenter @Inject constructor( ) roomName.value = room.name } - + LaunchedEffect(composerState.mode.relatedEventId) { + timelineState.eventSink(TimelineEvents.SetHighlightedEvent(composerState.mode.relatedEventId)) + } fun handleEvents(event: MessagesEvents) { when (event) { is MessagesEvents.HandleAction -> localCoroutineScope.handleTimelineAction(event.action, event.messageEvent, composerState) @@ -79,7 +82,7 @@ class MessagesPresenter @Inject constructor( fun CoroutineScope.handleTimelineAction( action: TimelineItemAction, - targetEvent: MessagesTimelineItemState.MessageEvent, + targetEvent: TimelineItem.MessageEvent, composerState: MessageComposerState, ) = launch { when (action) { @@ -95,21 +98,21 @@ class MessagesPresenter @Inject constructor( Timber.v("NotImplementedYet") } - private suspend fun handleActionRedact(event: MessagesTimelineItemState.MessageEvent) { + private suspend fun handleActionRedact(event: TimelineItem.MessageEvent) { room.redactEvent(event.id) } - private fun handleActionEdit(targetEvent: MessagesTimelineItemState.MessageEvent, composerState: MessageComposerState) { + private fun handleActionEdit(targetEvent: TimelineItem.MessageEvent, composerState: MessageComposerState) { val composerMode = MessageComposerMode.Edit( targetEvent.id, - (targetEvent.content as? MessagesTimelineItemTextBasedContent)?.body.orEmpty() + (targetEvent.content as? TimelineItemTextBasedContent)?.body.orEmpty() ) composerState.eventSink( MessageComposerEvents.SetMode(composerMode) ) } - private fun handleActionReply(targetEvent: MessagesTimelineItemState.MessageEvent, composerState: MessageComposerState) { + private fun handleActionReply(targetEvent: TimelineItem.MessageEvent, composerState: MessageComposerState) { val composerMode = MessageComposerMode.Reply(targetEvent.safeSenderName, targetEvent.id, "") composerState.eventSink( MessageComposerEvents.SetMode(composerMode) diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/MessagesState.kt b/features/messages/src/main/java/io/element/android/x/features/messages/MessagesState.kt index a7fcefb88f..f9cdcb2b90 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/MessagesState.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/MessagesState.kt @@ -10,10 +10,10 @@ import io.element.android.x.matrix.core.RoomId @Immutable data class MessagesState( val roomId: RoomId, - val roomName: String? = null, - val roomAvatar: AvatarData? = null, + val roomName: String?, + val roomAvatar: AvatarData?, val composerState: MessageComposerState, val timelineState: TimelineState, val actionListState: ActionListState, - val eventSink: (MessagesEvents) -> Unit = {} + val eventSink: (MessagesEvents) -> Unit ) diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/MessagesView.kt b/features/messages/src/main/java/io/element/android/x/features/messages/MessagesView.kt index 6e8a761b28..36ebf90e35 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/MessagesView.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/MessagesView.kt @@ -48,8 +48,10 @@ import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp @@ -57,11 +59,13 @@ import androidx.compose.ui.unit.sp import io.element.android.x.core.compose.LogCompositions 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.actionlist.TimelineItemAction +import io.element.android.x.features.messages.actionlist.ActionListEvents import io.element.android.x.features.messages.actionlist.ActionListView -import io.element.android.x.features.messages.model.MessagesTimelineItemState +import io.element.android.x.features.messages.actionlist.model.TimelineItemAction +import io.element.android.x.features.messages.timeline.model.TimelineItem import io.element.android.x.features.messages.textcomposer.MessageComposerView import io.element.android.x.features.messages.timeline.TimelineView +import kotlinx.coroutines.launch import timber.log.Timber @Composable @@ -76,8 +80,28 @@ fun MessagesView( initialValue = ModalBottomSheetValue.Hidden, ) val snackbarHostState = remember { SnackbarHostState() } + val focusManager = LocalFocusManager.current + val coroutineScope = rememberCoroutineScope() LogCompositions(tag = "MessagesScreen", msg = "Content") + + fun onMessageClicked(messageEvent: TimelineItem.MessageEvent) { + Timber.v("OnMessageClicked= ${messageEvent.id}") + } + + fun onMessageLongClicked(messageEvent: TimelineItem.MessageEvent) { + Timber.v("OnMessageLongClicked= ${messageEvent.id}") + focusManager.clearFocus(force = true) + state.actionListState.eventSink(ActionListEvents.ComputeForMessage(messageEvent)) + coroutineScope.launch { + itemActionsBottomSheetState.show() + } + } + + fun onActionSelected(action: TimelineItemAction, messageEvent: TimelineItem.MessageEvent) { + state.eventSink(MessagesEvents.HandleAction(action, messageEvent)) + } + Scaffold( modifier = modifier, contentWindowInsets = WindowInsets.statusBars, @@ -92,6 +116,8 @@ fun MessagesView( MessagesViewContent( state = state, modifier = Modifier.padding(padding), + onMessageClicked = ::onMessageClicked, + onMessageLongClicked = ::onMessageLongClicked ) }, snackbarHost = { @@ -102,10 +128,6 @@ fun MessagesView( }, ) - fun onActionSelected(action: TimelineItemAction, messageEvent: MessagesTimelineItemState.MessageEvent) { - state.eventSink(MessagesEvents.HandleAction(action, messageEvent)) - } - ActionListView( state = state.actionListState, modalBottomSheetState = itemActionsBottomSheetState, @@ -116,43 +138,32 @@ fun MessagesView( @Composable fun MessagesViewContent( state: MessagesState, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, + onMessageClicked: (TimelineItem.MessageEvent) -> Unit = {}, + onMessageLongClicked: (TimelineItem.MessageEvent) -> Unit = {}, ) { - - fun onMessageClicked(messageEvent: MessagesTimelineItemState.MessageEvent) { - Timber.v("OnMessageClicked= $messageEvent") - } - - fun onMessageLongClicked(messageEvent: MessagesTimelineItemState.MessageEvent) { - Timber.v("OnMessageLongClicked= $messageEvent") - } - Column( modifier = modifier .fillMaxSize() .navigationBarsPadding() .imePadding() ) { + // Hide timeline if composer is full screen if (!state.composerState.isFullScreen) { TimelineView( state = state.timelineState, - modifier = Modifier.fillMaxWidth(), - onMessageClicked = ::onMessageClicked, - onMessageLongClicked = ::onMessageLongClicked + modifier = Modifier.weight(1f), + onMessageClicked = onMessageClicked, + onMessageLongClicked = onMessageLongClicked ) } MessageComposerView( state = state.composerState, modifier = Modifier .fillMaxWidth() - .let { - if (state.composerState.isFullScreen) { - it.weight(1f, fill = false) - } else { - it.wrapContentHeight(Alignment.Bottom) - } - }, + .wrapContentHeight(Alignment.Bottom) ) + } } diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/actionlist/ActionListEvents.kt b/features/messages/src/main/java/io/element/android/x/features/messages/actionlist/ActionListEvents.kt index 07554c88b0..3cddfb7f90 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/actionlist/ActionListEvents.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/actionlist/ActionListEvents.kt @@ -1,8 +1,8 @@ package io.element.android.x.features.messages.actionlist -import io.element.android.x.features.messages.model.MessagesTimelineItemState +import io.element.android.x.features.messages.timeline.model.TimelineItem sealed interface ActionListEvents { object Clear : ActionListEvents - data class ComputeForMessage(val messageEvent: MessagesTimelineItemState.MessageEvent) : ActionListEvents + data class ComputeForMessage(val messageEvent: TimelineItem.MessageEvent) : ActionListEvents } diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/actionlist/ActionListPresenter.kt b/features/messages/src/main/java/io/element/android/x/features/messages/actionlist/ActionListPresenter.kt index f8fac36a89..e7e3020c4a 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/actionlist/ActionListPresenter.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/actionlist/ActionListPresenter.kt @@ -6,8 +6,9 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import io.element.android.x.architecture.Presenter -import io.element.android.x.features.messages.model.MessagesTimelineItemState -import io.element.android.x.features.messages.model.content.MessagesTimelineItemRedactedContent +import io.element.android.x.features.messages.actionlist.model.TimelineItemAction +import io.element.android.x.features.messages.timeline.model.TimelineItem +import io.element.android.x.features.messages.timeline.model.content.TimelineItemRedactedContent import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -37,10 +38,10 @@ class ActionListPresenter @Inject constructor() : Presenter { ) } - fun CoroutineScope.computeForMessage(messagesTimelineItemState: MessagesTimelineItemState.MessageEvent, target: MutableState) = launch { - target.value = ActionListState.Target.Loading(messagesTimelineItemState) + fun CoroutineScope.computeForMessage(timelineItem: TimelineItem.MessageEvent, target: MutableState) = launch { + target.value = ActionListState.Target.Loading(timelineItem) val actions = - if (messagesTimelineItemState.content is MessagesTimelineItemRedactedContent) { + if (timelineItem.content is TimelineItemRedactedContent) { emptyList() } else { mutableListOf( @@ -48,12 +49,12 @@ class ActionListPresenter @Inject constructor() : Presenter { TimelineItemAction.Forward, TimelineItemAction.Copy, ).also { - if (messagesTimelineItemState.isMine) { + if (timelineItem.isMine) { it.add(TimelineItemAction.Edit) it.add(TimelineItemAction.Redact) } } } - target.value = ActionListState.Target.Success(messagesTimelineItemState, actions.toImmutableList()) + target.value = ActionListState.Target.Success(timelineItem, actions.toImmutableList()) } } diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/actionlist/ActionListState.kt b/features/messages/src/main/java/io/element/android/x/features/messages/actionlist/ActionListState.kt index d81a794fa0..00c3ec59ab 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/actionlist/ActionListState.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/actionlist/ActionListState.kt @@ -17,20 +17,21 @@ package io.element.android.x.features.messages.actionlist import androidx.compose.runtime.Immutable -import io.element.android.x.features.messages.model.MessagesTimelineItemState +import io.element.android.x.features.messages.actionlist.model.TimelineItemAction +import io.element.android.x.features.messages.timeline.model.TimelineItem import kotlinx.collections.immutable.ImmutableList @Immutable data class ActionListState( - val target: Target = Target.None, - val eventSink: (ActionListEvents) -> Unit = {}, + val target: Target, + val eventSink: (ActionListEvents) -> Unit, ) { sealed interface Target { object None : Target - data class Loading(val messageEvent: MessagesTimelineItemState.MessageEvent) : Target + data class Loading(val messageEvent: TimelineItem.MessageEvent) : Target data class Success( - val messageEvent: MessagesTimelineItemState.MessageEvent, + val messageEvent: TimelineItem.MessageEvent, val actions: ImmutableList, ) : Target } diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/actionlist/ActionListView.kt b/features/messages/src/main/java/io/element/android/x/features/messages/actionlist/ActionListView.kt index 06a3824686..6f19db9562 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/actionlist/ActionListView.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/actionlist/ActionListView.kt @@ -26,7 +26,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import io.element.android.x.designsystem.components.VectorIcon -import io.element.android.x.features.messages.model.MessagesTimelineItemState +import io.element.android.x.features.messages.actionlist.model.TimelineItemAction +import io.element.android.x.features.messages.timeline.model.TimelineItem import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch @@ -34,7 +35,7 @@ import kotlinx.coroutines.launch fun ActionListView( state: ActionListState, modalBottomSheetState: ModalBottomSheetState, - onActionSelected: (action: TimelineItemAction, MessagesTimelineItemState.MessageEvent) -> Unit, + onActionSelected: (action: TimelineItemAction, TimelineItem.MessageEvent) -> Unit, modifier: Modifier = Modifier ) { val coroutineScope = rememberCoroutineScope() @@ -48,7 +49,7 @@ fun ActionListView( fun onItemActionClicked( itemAction: TimelineItemAction, - targetItem: MessagesTimelineItemState.MessageEvent + targetItem: TimelineItem.MessageEvent ) { onActionSelected(itemAction, targetItem) coroutineScope.launch { @@ -75,7 +76,7 @@ fun ActionListView( private fun SheetContent( state: ActionListState, modifier: Modifier = Modifier, - onActionClicked: (TimelineItemAction, MessagesTimelineItemState.MessageEvent) -> Unit = { _, _ -> }, + onActionClicked: (TimelineItemAction, TimelineItem.MessageEvent) -> Unit = { _, _ -> }, ) { when (val target = state.target) { is ActionListState.Target.Loading, diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/actionlist/TimelineItemAction.kt b/features/messages/src/main/java/io/element/android/x/features/messages/actionlist/model/TimelineItemAction.kt similarity index 92% rename from features/messages/src/main/java/io/element/android/x/features/messages/actionlist/TimelineItemAction.kt rename to features/messages/src/main/java/io/element/android/x/features/messages/actionlist/model/TimelineItemAction.kt index 36ce779ab3..4e23eb34f0 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/actionlist/TimelineItemAction.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/actionlist/model/TimelineItemAction.kt @@ -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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.x.features.messages.actionlist +package io.element.android.x.features.messages.actionlist.model import androidx.annotation.DrawableRes import androidx.compose.runtime.Immutable diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/textcomposer/MessageComposerPresenter.kt b/features/messages/src/main/java/io/element/android/x/features/messages/textcomposer/MessageComposerPresenter.kt index 2c51a0199b..65652da85e 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/textcomposer/MessageComposerPresenter.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/textcomposer/MessageComposerPresenter.kt @@ -1,12 +1,13 @@ package io.element.android.x.features.messages.textcomposer import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable import io.element.android.x.architecture.Presenter +import io.element.android.x.core.data.StableCharSequence import io.element.android.x.core.data.toStableCharSequence -import io.element.android.x.matrix.MatrixClient import io.element.android.x.matrix.room.MatrixRoom import io.element.android.x.textcomposer.MessageComposerMode import kotlinx.coroutines.CoroutineScope @@ -15,7 +16,6 @@ import javax.inject.Inject class MessageComposerPresenter @Inject constructor( private val appCoroutineScope: CoroutineScope, - private val client: MatrixClient, private val room: MatrixRoom ) : Presenter { @@ -24,25 +24,32 @@ class MessageComposerPresenter @Inject constructor( val isFullScreen = rememberSaveable { mutableStateOf(false) } - val text: MutableState = rememberSaveable { - mutableStateOf("") + val text: MutableState = rememberSaveable { + mutableStateOf(StableCharSequence("")) } val composerMode: MutableState = rememberSaveable { mutableStateOf(MessageComposerMode.Normal("")) } + LaunchedEffect(composerMode.value) { + when (val modeValue = composerMode.value) { + is MessageComposerMode.Edit -> text.value = modeValue.defaultContent.toStableCharSequence() + else -> Unit + } + } + fun handleEvents(event: MessageComposerEvents) { when (event) { MessageComposerEvents.ToggleFullScreenState -> isFullScreen.value = !isFullScreen.value - is MessageComposerEvents.UpdateText -> text.value = event.text + is MessageComposerEvents.UpdateText -> text.value = event.text.toStableCharSequence() MessageComposerEvents.CloseSpecialMode -> composerMode.setToNormal() - is MessageComposerEvents.SendMessage -> appCoroutineScope.sendMessage(event.message, composerMode) + is MessageComposerEvents.SendMessage -> appCoroutineScope.sendMessage(event.message, composerMode, text) is MessageComposerEvents.SetMode -> composerMode.value = event.composerMode } } return MessageComposerState( - text = text.value.toStableCharSequence(), + text = text.value, isFullScreen = isFullScreen.value, mode = composerMode.value, eventSink = ::handleEvents @@ -53,9 +60,10 @@ class MessageComposerPresenter @Inject constructor( value = MessageComposerMode.Normal("") } - private fun CoroutineScope.sendMessage(text: String, composerMode: MutableState) = launch { + private fun CoroutineScope.sendMessage(text: String, composerMode: MutableState, textState: MutableState) = launch { val capturedMode = composerMode.value // Reset composer right away + textState.value = "".toStableCharSequence() composerMode.setToNormal() when (capturedMode) { is MessageComposerMode.Normal -> room.sendMessage(text) diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/textcomposer/MessageComposerState.kt b/features/messages/src/main/java/io/element/android/x/features/messages/textcomposer/MessageComposerState.kt index dbd92f6139..44c8e73d7e 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/textcomposer/MessageComposerState.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/textcomposer/MessageComposerState.kt @@ -22,16 +22,10 @@ import io.element.android.x.textcomposer.MessageComposerMode @Immutable data class MessageComposerState( - // val roomId: String, - // val canSendMessage: CanSendStatus = CanSendStatus.Allowed, - val isSendButtonVisible: Boolean = false, - val rootThreadEventId: String? = null, - val startsThread: Boolean = false, - // val sendMode: SendMode = SendMode.Regular("", false), - // val voiceRecordingUiState: VoiceMessageRecorderView.RecordingUiState = VoiceMessageRecorderView.RecordingUiState.Idle, - // val voiceBroadcastState: VoiceBroadcastState? = null, - val text: StableCharSequence? = null, - val isFullScreen: Boolean = false, - val mode: MessageComposerMode = MessageComposerMode.Normal(""), - val eventSink: (MessageComposerEvents) -> Unit = {} -) + val text: StableCharSequence?, + val isFullScreen: Boolean, + val mode: MessageComposerMode, + val eventSink: (MessageComposerEvents) -> Unit +) { + val isSendButtonVisible: Boolean = text?.charSequence.isNullOrEmpty().not() +} diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/textcomposer/MessageComposerView.kt b/features/messages/src/main/java/io/element/android/x/features/messages/textcomposer/MessageComposerView.kt index 74402a9e6d..cf417d5598 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/textcomposer/MessageComposerView.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/textcomposer/MessageComposerView.kt @@ -7,7 +7,7 @@ import io.element.android.x.textcomposer.TextComposer @Composable fun MessageComposerView( state: MessageComposerState, - modifier: Modifier + modifier: Modifier = Modifier, ) { fun onFullscreenToggle() { diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/MessageTimelineItemStateFactory.kt b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/TimelineItemsFactory.kt similarity index 70% rename from features/messages/src/main/java/io/element/android/x/features/messages/MessageTimelineItemStateFactory.kt rename to features/messages/src/main/java/io/element/android/x/features/messages/timeline/TimelineItemsFactory.kt index 68bf073de7..ed9594aa8c 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/MessageTimelineItemStateFactory.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/TimelineItemsFactory.kt @@ -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. @@ -14,29 +14,31 @@ * limitations under the License. */ -package io.element.android.x.features.messages +package io.element.android.x.features.messages.timeline import androidx.recyclerview.widget.DiffUtil import io.element.android.x.designsystem.components.avatar.AvatarSize -import io.element.android.x.features.messages.diff.CacheInvalidator -import io.element.android.x.features.messages.diff.MatrixTimelineItemsDiffCallback -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.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.features.messages.timeline.diff.CacheInvalidator +import io.element.android.x.features.messages.timeline.diff.MatrixTimelineItemsDiffCallback +import io.element.android.x.features.messages.timeline.model.AggregatedReaction +import io.element.android.x.features.messages.timeline.model.MessagesItemGroupPosition +import io.element.android.x.features.messages.timeline.model.TimelineItemReactions +import io.element.android.x.features.messages.timeline.model.TimelineItem +import io.element.android.x.features.messages.timeline.model.content.TimelineItemContent +import io.element.android.x.features.messages.timeline.model.content.TimelineItemEmoteContent +import io.element.android.x.features.messages.timeline.model.content.TimelineItemEncryptedContent +import io.element.android.x.features.messages.timeline.model.content.TimelineItemImageContent +import io.element.android.x.features.messages.timeline.model.content.TimelineItemNoticeContent +import io.element.android.x.features.messages.timeline.model.content.TimelineItemRedactedContent +import io.element.android.x.features.messages.timeline.model.content.TimelineItemTextContent +import io.element.android.x.features.messages.timeline.model.content.TimelineItemUnknownContent +import io.element.android.x.features.messages.timeline.util.invalidateLast +import io.element.android.x.matrix.core.EventId 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 io.element.android.x.matrix.ui.MatrixItemHelper +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -50,24 +52,25 @@ import org.matrix.rustcomponents.sdk.FormattedBody import org.matrix.rustcomponents.sdk.MessageFormat import org.matrix.rustcomponents.sdk.MessageType import timber.log.Timber +import javax.inject.Inject import kotlin.system.measureTimeMillis -class MessageTimelineItemStateFactory( +class TimelineItemsFactory @Inject constructor( private val matrixItemHelper: MatrixItemHelper, private val room: MatrixRoom, private val dispatcher: CoroutineDispatcher, ) { - private val timelineItemStates = MutableStateFlow>(emptyList()) - private val timelineItemStatesCache = arrayListOf() + private val timelineItems = MutableStateFlow>(emptyList()) + private val timelineItemsCache = arrayListOf() // Items from rust sdk, used for diffing - private var timelineItems: List = emptyList() + private var matrixTimelineItems: List = emptyList() private val lock = Mutex() - private val cacheInvalidator = CacheInvalidator(timelineItemStatesCache) + private val cacheInvalidator = CacheInvalidator(timelineItemsCache) - fun flow(): StateFlow> = timelineItemStates.asStateFlow() + fun flow(): StateFlow> = timelineItems.asStateFlow() suspend fun replaceWith( timelineItems: List, @@ -83,17 +86,17 @@ class MessageTimelineItemStateFactory( ) = withContext(dispatcher) { lock.withLock { // Makes sure to invalidate last as we need to recompute some data (like groupPosition) - timelineItemStatesCache.invalidateLast() - timelineItemStatesCache.add(null) - timelineItems = timelineItems + timelineItem - buildAndEmitTimelineItemStates(timelineItems) + timelineItemsCache.invalidateLast() + timelineItemsCache.add(null) + matrixTimelineItems = matrixTimelineItems + timelineItem + buildAndEmitTimelineItemStates(matrixTimelineItems) } } private suspend fun buildAndEmitTimelineItemStates(timelineItems: List) { - val newTimelineItemStates = ArrayList() - for (index in timelineItemStatesCache.indices.reversed()) { - val cacheItem = timelineItemStatesCache[index] + val newTimelineItemStates = ArrayList() + for (index in timelineItemsCache.indices.reversed()) { + val cacheItem = timelineItemsCache[index] if (cacheItem == null) { buildAndCacheItem(timelineItems, index)?.also { timelineItemState -> newTimelineItemStates.add(timelineItemState) @@ -102,18 +105,18 @@ class MessageTimelineItemStateFactory( newTimelineItemStates.add(cacheItem) } } - timelineItemStates.emit(newTimelineItemStates) + this.timelineItems.emit(newTimelineItemStates) } private fun calculateAndApplyDiff(newTimelineItems: List) { val timeToDiff = measureTimeMillis { val diffCallback = MatrixTimelineItemsDiffCallback( - oldList = timelineItems, + oldList = matrixTimelineItems, newList = newTimelineItems ) val diffResult = DiffUtil.calculateDiff(diffCallback, false) - timelineItems = newTimelineItems + matrixTimelineItems = newTimelineItems diffResult.dispatchUpdatesTo(cacheInvalidator) } Timber.v("Time to apply diff on new list of ${newTimelineItems.size} items: $timeToDiff ms") @@ -122,7 +125,7 @@ class MessageTimelineItemStateFactory( private suspend fun buildAndCacheItem( timelineItems: List, index: Int - ): MessagesTimelineItemState? { + ): TimelineItem? { val timelineItemState = when (val currentTimelineItem = timelineItems[index]) { is MatrixTimelineItem.Event -> { @@ -132,12 +135,12 @@ class MessageTimelineItemStateFactory( timelineItems, ) } - is MatrixTimelineItem.Virtual -> MessagesTimelineItemState.Virtual( + is MatrixTimelineItem.Virtual -> TimelineItem.Virtual( "virtual_item_$index" ) MatrixTimelineItem.Other -> null } - timelineItemStatesCache[index] = timelineItemState + timelineItemsCache[index] = timelineItemState return timelineItemState } @@ -145,7 +148,7 @@ class MessageTimelineItemStateFactory( currentTimelineItem: MatrixTimelineItem.Event, index: Int, timelineItems: List, - ): MessagesTimelineItemState.MessageEvent { + ): TimelineItem.MessageEvent { val currentSender = currentTimelineItem.event.sender() val groupPosition = computeGroupPosition(currentTimelineItem, timelineItems, index) @@ -157,8 +160,8 @@ class MessageTimelineItemStateFactory( url = senderAvatarUrl, size = AvatarSize.SMALL ) - return MessagesTimelineItemState.MessageEvent( - id = currentTimelineItem.uniqueId, + return TimelineItem.MessageEvent( + id = EventId(currentTimelineItem.uniqueId), senderId = currentSender, senderDisplayName = senderDisplayName, senderAvatar = senderAvatarData, @@ -169,24 +172,24 @@ class MessageTimelineItemStateFactory( ) } - private fun MatrixTimelineItem.Event.computeReactionsState(): MessagesItemReactionState { + private fun MatrixTimelineItem.Event.computeReactionsState(): TimelineItemReactions { val aggregatedReactions = event.reactions().map { AggregatedReaction(key = it.key, count = it.count.toString(), isHighlighted = false) } - return MessagesItemReactionState(aggregatedReactions) + return TimelineItemReactions(aggregatedReactions.toImmutableList()) } - private fun MatrixTimelineItem.Event.computeContent(): MessagesTimelineItemContent { + private fun MatrixTimelineItem.Event.computeContent(): TimelineItemContent { val content = event.content() content.asUnableToDecrypt()?.let { encryptedMessage -> - return MessagesTimelineItemEncryptedContent(encryptedMessage) + return TimelineItemEncryptedContent(encryptedMessage) } if (content.isRedactedMessage()) { - return MessagesTimelineItemRedactedContent + return TimelineItemRedactedContent } val contentAsMessage = content.asMessage() return when (val messageType = contentAsMessage?.msgtype()) { - is MessageType.Emote -> MessagesTimelineItemEmoteContent( + is MessageType.Emote -> TimelineItemEmoteContent( body = messageType.content.body, htmlDocument = messageType.content.formatted?.toHtmlDocument() ) @@ -198,7 +201,7 @@ class MessageTimelineItemStateFactory( } else { 0.7f } - MessagesTimelineItemImageContent( + TimelineItemImageContent( body = messageType.content.body, imageMeta = MediaResolver.Meta( source = messageType.content.source, @@ -208,15 +211,15 @@ class MessageTimelineItemStateFactory( aspectRatio = aspectRatio ) } - is MessageType.Notice -> MessagesTimelineItemNoticeContent( + is MessageType.Notice -> TimelineItemNoticeContent( body = messageType.content.body, htmlDocument = messageType.content.formatted?.toHtmlDocument() ) - is MessageType.Text -> MessagesTimelineItemTextContent( + is MessageType.Text -> TimelineItemTextContent( body = messageType.content.body, htmlDocument = messageType.content.formatted?.toHtmlDocument() ) - else -> MessagesTimelineItemUnknownContent + else -> TimelineItemUnknownContent } } diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/timeline/TimelinePresenter.kt b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/TimelinePresenter.kt index 35167d82d8..6235f6784a 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/timeline/TimelinePresenter.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/TimelinePresenter.kt @@ -8,15 +8,14 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable -import io.element.android.x.architecture.Async import io.element.android.x.architecture.Presenter -import io.element.android.x.features.messages.MessageTimelineItemStateFactory import io.element.android.x.matrix.MatrixClient import io.element.android.x.matrix.core.EventId import io.element.android.x.matrix.room.MatrixRoom import io.element.android.x.matrix.timeline.MatrixTimeline import io.element.android.x.matrix.timeline.MatrixTimelineItem import io.element.android.x.matrix.ui.MatrixItemHelper +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.launchIn @@ -34,13 +33,13 @@ class TimelinePresenter @Inject constructor( private val timeline = room.timeline() private val matrixItemHelper = MatrixItemHelper(client) - private val messageTimelineItemStateFactory = - MessageTimelineItemStateFactory(matrixItemHelper, room, Dispatchers.Default) + private val timelineItemsFactory = + TimelineItemsFactory(matrixItemHelper, room, Dispatchers.Default) - private class TimelineCallback(private val coroutineScope: CoroutineScope, private val messageTimelineItemStateFactory: MessageTimelineItemStateFactory) : MatrixTimeline.Callback { + private class TimelineCallback(private val coroutineScope: CoroutineScope, private val timelineItemsFactory: TimelineItemsFactory) : MatrixTimeline.Callback { override fun onPushedTimelineItem(timelineItem: MatrixTimelineItem) { coroutineScope.launch { - messageTimelineItemStateFactory.pushItem(timelineItem) + timelineItemsFactory.pushItem(timelineItem) } } } @@ -55,7 +54,7 @@ class TimelinePresenter @Inject constructor( val highlightedEventId: MutableState = rememberSaveable { mutableStateOf(null) } - val timelineItems = messageTimelineItemStateFactory + val timelineItems = timelineItemsFactory .flow() .collectAsState(emptyList()) @@ -69,12 +68,12 @@ class TimelinePresenter @Inject constructor( LaunchedEffect(Unit) { timeline .timelineItems() - .onEach(messageTimelineItemStateFactory::replaceWith) + .onEach(timelineItemsFactory::replaceWith) .launchIn(this) } DisposableEffect(Unit) { - timeline.callback = TimelineCallback(localCoroutineScope, messageTimelineItemStateFactory) + timeline.callback = TimelineCallback(localCoroutineScope, timelineItemsFactory) timeline.initialize() onDispose { timeline.callback = null @@ -84,13 +83,13 @@ class TimelinePresenter @Inject constructor( return TimelineState( highlightedEventId = highlightedEventId.value, - timelineItems = Async.Success(timelineItems.value), + timelineItems = timelineItems.value.toImmutableList(), hasMoreToLoad = hasMoreToLoad.value, eventSink = ::handleEvents ) } - fun CoroutineScope.loadMore(hasMoreToLoad: MutableState) = launch { + private fun CoroutineScope.loadMore(hasMoreToLoad: MutableState) = launch { timeline.paginateBackwards(PAGINATION_COUNT) hasMoreToLoad.value = timeline.hasMoreToLoad } diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/timeline/TimelineState.kt b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/TimelineState.kt index 605bd43fb8..c4ea612334 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/timeline/TimelineState.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/TimelineState.kt @@ -17,14 +17,14 @@ package io.element.android.x.features.messages.timeline import androidx.compose.runtime.Immutable -import io.element.android.x.architecture.Async -import io.element.android.x.features.messages.model.MessagesTimelineItemState +import io.element.android.x.features.messages.timeline.model.TimelineItem import io.element.android.x.matrix.core.EventId +import kotlinx.collections.immutable.ImmutableList @Immutable data class TimelineState( - val timelineItems: Async> = Async.Uninitialized, - val hasMoreToLoad: Boolean = true, - val highlightedEventId: EventId? = null, - val eventSink: (TimelineEvents) -> Unit = {} + val timelineItems: ImmutableList, + val hasMoreToLoad: Boolean, + val highlightedEventId: EventId?, + val eventSink: (TimelineEvents) -> Unit ) diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/timeline/TimelineView.kt b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/TimelineView.kt index b04cc2a1a3..fa938f58dc 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/timeline/TimelineView.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/TimelineView.kt @@ -42,32 +42,31 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex -import io.element.android.x.architecture.Async import io.element.android.x.core.compose.PairCombinedPreviewParameter 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.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.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.timeline.model.AggregatedReaction +import io.element.android.x.features.messages.timeline.model.MessagesItemGroupPosition +import io.element.android.x.features.messages.timeline.model.TimelineItemGroupPositionProvider +import io.element.android.x.features.messages.timeline.model.TimelineItemReactions +import io.element.android.x.features.messages.timeline.model.TimelineItem +import io.element.android.x.features.messages.timeline.model.content.TimelineItemContent +import io.element.android.x.features.messages.timeline.model.content.MessagesTimelineItemContentProvider +import io.element.android.x.features.messages.timeline.model.content.TimelineItemEncryptedContent +import io.element.android.x.features.messages.timeline.model.content.TimelineItemImageContent +import io.element.android.x.features.messages.timeline.model.content.TimelineItemRedactedContent +import io.element.android.x.features.messages.timeline.model.content.TimelineItemTextBasedContent +import io.element.android.x.features.messages.timeline.model.content.TimelineItemUnknownContent import io.element.android.x.features.messages.timeline.components.MessageEventBubble -import io.element.android.x.features.messages.timeline.components.MessagesReactionsView -import io.element.android.x.features.messages.timeline.components.MessagesTimelineItemEncryptedView -import io.element.android.x.features.messages.timeline.components.MessagesTimelineItemImageView -import io.element.android.x.features.messages.timeline.components.MessagesTimelineItemRedactedView -import io.element.android.x.features.messages.timeline.components.MessagesTimelineItemTextView -import io.element.android.x.features.messages.timeline.components.MessagesTimelineItemUnknownView +import io.element.android.x.features.messages.timeline.components.TimelineItemReactionsView +import io.element.android.x.features.messages.timeline.components.TimelineItemEncryptedView +import io.element.android.x.features.messages.timeline.components.TimelineItemImageView +import io.element.android.x.features.messages.timeline.components.TimelineItemRedactedView +import io.element.android.x.features.messages.timeline.components.TimelineItemTextView +import io.element.android.x.features.messages.timeline.components.TimelineItemUnknownView +import io.element.android.x.matrix.core.EventId import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.launch @@ -75,13 +74,11 @@ import kotlinx.coroutines.launch fun TimelineView( state: TimelineState, modifier: Modifier = Modifier, - onMessageClicked: (MessagesTimelineItemState.MessageEvent) -> Unit = {}, - onMessageLongClicked: (MessagesTimelineItemState.MessageEvent) -> Unit = {}, + onMessageClicked: (TimelineItem.MessageEvent) -> Unit = {}, + onMessageLongClicked: (TimelineItem.MessageEvent) -> Unit = {}, ) { val lazyListState = rememberLazyListState() - val timelineItems = state.timelineItems.dataOrNull().orEmpty().toImmutableList() - - Box(modifier = modifier.fillMaxWidth()) { + Box(modifier = modifier) { LazyColumn( modifier = Modifier.fillMaxSize(), state = lazyListState, @@ -90,7 +87,7 @@ fun TimelineView( reverseLayout = true ) { items( - items = timelineItems, + items = state.timelineItems, contentType = { timelineItem -> timelineItem.contentType() }, key = { timelineItem -> timelineItem.key() }, ) { timelineItem -> @@ -114,36 +111,36 @@ fun TimelineView( TimelineScrollHelper( lazyListState = lazyListState, - timelineItems = timelineItems, + timelineItems = state.timelineItems, onLoadMore = ::onReachedLoadMore ) } } -private fun MessagesTimelineItemState.key(): String { +private fun TimelineItem.key(): String { return when (this) { - is MessagesTimelineItemState.MessageEvent -> id - is MessagesTimelineItemState.Virtual -> id + is TimelineItem.MessageEvent -> id.value + is TimelineItem.Virtual -> id } } -private fun MessagesTimelineItemState.contentType(): Int { +private fun TimelineItem.contentType(): Int { return when (this) { - is MessagesTimelineItemState.MessageEvent -> 0 - is MessagesTimelineItemState.Virtual -> 1 + is TimelineItem.MessageEvent -> 0 + is TimelineItem.Virtual -> 1 } } @Composable fun TimelineItemRow( - timelineItem: MessagesTimelineItemState, + timelineItem: TimelineItem, isHighlighted: Boolean, - onClick: (MessagesTimelineItemState.MessageEvent) -> Unit, - onLongClick: (MessagesTimelineItemState.MessageEvent) -> Unit, + onClick: (TimelineItem.MessageEvent) -> Unit, + onLongClick: (TimelineItem.MessageEvent) -> Unit, ) { when (timelineItem) { - is MessagesTimelineItemState.Virtual -> return - is MessagesTimelineItemState.MessageEvent -> MessageEventRow( + is TimelineItem.Virtual -> return + is TimelineItem.MessageEvent -> MessageEventRow( messageEvent = timelineItem, isHighlighted = isHighlighted, onClick = { onClick(timelineItem) }, @@ -154,7 +151,7 @@ fun TimelineItemRow( @Composable fun MessageEventRow( - messageEvent: MessagesTimelineItemState.MessageEvent, + messageEvent: TimelineItem.MessageEvent, isHighlighted: Boolean, onClick: () -> Unit, onLongClick: () -> Unit, @@ -197,32 +194,32 @@ fun MessageEventRow( ) { val contentModifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp) when (messageEvent.content) { - is MessagesTimelineItemEncryptedContent -> MessagesTimelineItemEncryptedView( + is TimelineItemEncryptedContent -> TimelineItemEncryptedView( content = messageEvent.content, modifier = contentModifier ) - is MessagesTimelineItemRedactedContent -> MessagesTimelineItemRedactedView( + is TimelineItemRedactedContent -> TimelineItemRedactedView( content = messageEvent.content, modifier = contentModifier ) - is MessagesTimelineItemTextBasedContent -> MessagesTimelineItemTextView( + is TimelineItemTextBasedContent -> TimelineItemTextView( content = messageEvent.content, interactionSource = interactionSource, modifier = contentModifier, onTextClicked = onClick, onTextLongClicked = onLongClick ) - is MessagesTimelineItemUnknownContent -> MessagesTimelineItemUnknownView( + is TimelineItemUnknownContent -> TimelineItemUnknownView( content = messageEvent.content, modifier = contentModifier ) - is MessagesTimelineItemImageContent -> MessagesTimelineItemImageView( + is TimelineItemImageContent -> TimelineItemImageView( content = messageEvent.content, modifier = contentModifier ) } } - MessagesReactionsView( + TimelineItemReactionsView( reactionsState = messageEvent.reactionsState, modifier = Modifier .zIndex(1f) @@ -264,7 +261,7 @@ private fun MessageSenderInformation( @Composable internal fun BoxScope.TimelineScrollHelper( lazyListState: LazyListState, - timelineItems: ImmutableList, + timelineItems: ImmutableList, onLoadMore: () -> Unit = {}, ) { val coroutineScope = rememberCoroutineScope() @@ -338,8 +335,8 @@ internal fun TimelineLoadingMoreIndicator() { } class MessagesItemGroupPositionToMessagesTimelineItemContentProvider : - PairCombinedPreviewParameter( - MessagesItemGroupPositionProvider() to MessagesTimelineItemContentProvider() + PairCombinedPreviewParameter( + TimelineItemGroupPositionProvider() to MessagesTimelineItemContentProvider() ) @Suppress("PreviewPublic") @@ -347,7 +344,7 @@ class MessagesItemGroupPositionToMessagesTimelineItemContentProvider : @Composable fun TimelineItemsPreview( @PreviewParameter(MessagesTimelineItemContentProvider::class) - content: MessagesTimelineItemContent + content: TimelineItemContent ) { val timelineItems = persistentListOf( // 3 items (First Middle Last) with isMine = false @@ -385,23 +382,26 @@ fun TimelineItemsPreview( ) TimelineView( state = TimelineState( - timelineItems = Async.Success(timelineItems) + timelineItems = timelineItems, + hasMoreToLoad = true, + highlightedEventId = null, + eventSink = {} ) ) } private fun createMessageEvent( isMine: Boolean, - content: MessagesTimelineItemContent, + content: TimelineItemContent, groupPosition: MessagesItemGroupPosition -): MessagesTimelineItemState { - return MessagesTimelineItemState.MessageEvent( - id = Math.random().toString(), +): TimelineItem { + return TimelineItem.MessageEvent( + id = EventId(Math.random().toString()), senderId = "senderId", senderAvatar = AvatarData("sender"), content = content, - reactionsState = MessagesItemReactionState( - listOf( + reactionsState = TimelineItemReactions( + persistentListOf( AggregatedReaction("👍", "1") ) ), diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/MessageEventBubble.kt b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/MessageEventBubble.kt index 447e1bdd60..7466d38e1a 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/MessageEventBubble.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/MessageEventBubble.kt @@ -36,7 +36,7 @@ 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 +import io.element.android.x.features.messages.timeline.model.MessagesItemGroupPosition private val BUBBLE_RADIUS = 16.dp diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/MessagesTimelineItemEncryptedView.kt b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/TimelineItemEncryptedView.kt similarity index 82% rename from features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/MessagesTimelineItemEncryptedView.kt rename to features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/TimelineItemEncryptedView.kt index cfccdfae6b..0b037c6f1f 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/MessagesTimelineItemEncryptedView.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/TimelineItemEncryptedView.kt @@ -20,14 +20,14 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Warning import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import io.element.android.x.features.messages.model.content.MessagesTimelineItemEncryptedContent +import io.element.android.x.features.messages.timeline.model.content.TimelineItemEncryptedContent @Composable -fun MessagesTimelineItemEncryptedView( - content: MessagesTimelineItemEncryptedContent, +fun TimelineItemEncryptedView( + content: TimelineItemEncryptedContent, modifier: Modifier = Modifier ) { - MessagesTimelineItemInformativeView( + TimelineItemInformativeView( text = "Decryption error", iconDescription = "Warning", icon = Icons.Default.Warning, diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/MessagesTimelineItemImageView.kt b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/TimelineItemImageView.kt similarity index 92% rename from features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/MessagesTimelineItemImageView.kt rename to features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/TimelineItemImageView.kt index 53647ff652..a630959bcb 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/MessagesTimelineItemImageView.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/TimelineItemImageView.kt @@ -33,11 +33,11 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import coil.compose.AsyncImage import coil.request.ImageRequest -import io.element.android.x.features.messages.model.content.MessagesTimelineItemImageContent +import io.element.android.x.features.messages.timeline.model.content.TimelineItemImageContent @Composable -fun MessagesTimelineItemImageView( - content: MessagesTimelineItemImageContent, +fun TimelineItemImageView( + content: TimelineItemImageContent, modifier: Modifier = Modifier ) { val widthPercent = if (content.aspectRatio > 1f) { diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/MessagesTimelineItemInformativeView.kt b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/TimelineItemInformativeView.kt similarity index 97% rename from features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/MessagesTimelineItemInformativeView.kt rename to features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/TimelineItemInformativeView.kt index 200b17374a..6c876f9103 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/MessagesTimelineItemInformativeView.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/TimelineItemInformativeView.kt @@ -32,7 +32,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @Composable -fun MessagesTimelineItemInformativeView( +fun TimelineItemInformativeView( text: String, iconDescription: String, icon: ImageVector, diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/MessagesReactionsView.kt b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/TimelineItemReactionsView.kt similarity index 91% rename from features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/MessagesReactionsView.kt rename to features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/TimelineItemReactionsView.kt index 14b5cf99e2..8cc75befea 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/MessagesReactionsView.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/TimelineItemReactionsView.kt @@ -32,12 +32,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.google.accompanist.flowlayout.FlowRow -import io.element.android.x.features.messages.model.AggregatedReaction -import io.element.android.x.features.messages.model.MessagesItemReactionState +import io.element.android.x.features.messages.timeline.model.AggregatedReaction +import io.element.android.x.features.messages.timeline.model.TimelineItemReactions @Composable -fun MessagesReactionsView( - reactionsState: MessagesItemReactionState, +fun TimelineItemReactionsView( + reactionsState: TimelineItemReactions, modifier: Modifier = Modifier, ) { if (reactionsState.reactions.isEmpty()) return diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/MessagesTimelineItemRedactedView.kt b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/TimelineItemRedactedView.kt similarity index 76% rename from features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/MessagesTimelineItemRedactedView.kt rename to features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/TimelineItemRedactedView.kt index e3ef130d50..183b44de74 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/MessagesTimelineItemRedactedView.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/TimelineItemRedactedView.kt @@ -20,15 +20,14 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Delete import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import io.element.android.x.features.messages.model.content.MessagesTimelineItemRedactedContent -import io.element.android.x.features.messages.timeline.components.MessagesTimelineItemInformativeView +import io.element.android.x.features.messages.timeline.model.content.TimelineItemRedactedContent @Composable -fun MessagesTimelineItemRedactedView( - content: MessagesTimelineItemRedactedContent, +fun TimelineItemRedactedView( + content: TimelineItemRedactedContent, modifier: Modifier = Modifier ) { - MessagesTimelineItemInformativeView( + TimelineItemInformativeView( text = "This message has been deleted", iconDescription = "Delete", icon = Icons.Default.Delete, diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/MessagesTimelineItemTextView.kt b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/TimelineItemTextView.kt similarity index 94% rename from features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/MessagesTimelineItemTextView.kt rename to features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/TimelineItemTextView.kt index 9bc80e0de5..4751f2ae0d 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/MessagesTimelineItemTextView.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/TimelineItemTextView.kt @@ -31,11 +31,11 @@ import androidx.core.text.util.LinkifyCompat import io.element.android.x.designsystem.LinkColor import io.element.android.x.designsystem.components.ClickableLinkText import io.element.android.x.features.messages.timeline.components.html.HtmlDocument -import io.element.android.x.features.messages.model.content.MessagesTimelineItemTextBasedContent +import io.element.android.x.features.messages.timeline.model.content.TimelineItemTextBasedContent @Composable -fun MessagesTimelineItemTextView( - content: MessagesTimelineItemTextBasedContent, +fun TimelineItemTextView( + content: TimelineItemTextBasedContent, interactionSource: MutableInteractionSource, modifier: Modifier = Modifier, onTextClicked: () -> Unit = {}, diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/MessagesTimelineItemUnknownView.kt b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/TimelineItemUnknownView.kt similarity index 82% rename from features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/MessagesTimelineItemUnknownView.kt rename to features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/TimelineItemUnknownView.kt index 2b7ac2f85e..88355abd90 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/MessagesTimelineItemUnknownView.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/components/TimelineItemUnknownView.kt @@ -20,14 +20,14 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Info import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import io.element.android.x.features.messages.model.content.MessagesTimelineItemUnknownContent +import io.element.android.x.features.messages.timeline.model.content.TimelineItemUnknownContent @Composable -fun MessagesTimelineItemUnknownView( - content: MessagesTimelineItemUnknownContent, +fun TimelineItemUnknownView( + content: TimelineItemUnknownContent, modifier: Modifier = Modifier ) { - MessagesTimelineItemInformativeView( + TimelineItemInformativeView( text = "Event not handled by EAX", iconDescription = "Info", icon = Icons.Default.Info, diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/diff/CacheInvalidator.kt b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/diff/CacheInvalidator.kt similarity index 86% rename from features/messages/src/main/java/io/element/android/x/features/messages/diff/CacheInvalidator.kt rename to features/messages/src/main/java/io/element/android/x/features/messages/timeline/diff/CacheInvalidator.kt index a904951239..031d871033 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/diff/CacheInvalidator.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/diff/CacheInvalidator.kt @@ -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. @@ -14,14 +14,14 @@ * limitations under the License. */ -package io.element.android.x.features.messages.diff +package io.element.android.x.features.messages.timeline.diff import androidx.recyclerview.widget.ListUpdateCallback -import io.element.android.x.features.messages.model.MessagesTimelineItemState -import io.element.android.x.features.messages.util.invalidateLast +import io.element.android.x.features.messages.timeline.model.TimelineItem +import io.element.android.x.features.messages.timeline.util.invalidateLast import timber.log.Timber -internal class CacheInvalidator(private val itemStatesCache: MutableList) : +internal class CacheInvalidator(private val itemStatesCache: MutableList) : ListUpdateCallback { override fun onChanged(position: Int, count: Int, payload: Any?) { diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/diff/MatrixTimelineItemsDiffCallback.kt b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/diff/MatrixTimelineItemsDiffCallback.kt similarity index 96% rename from features/messages/src/main/java/io/element/android/x/features/messages/diff/MatrixTimelineItemsDiffCallback.kt rename to features/messages/src/main/java/io/element/android/x/features/messages/timeline/diff/MatrixTimelineItemsDiffCallback.kt index e0198d626a..45b754043a 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/diff/MatrixTimelineItemsDiffCallback.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/diff/MatrixTimelineItemsDiffCallback.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.x.features.messages.diff +package io.element.android.x.features.messages.timeline.diff import androidx.recyclerview.widget.DiffUtil import io.element.android.x.matrix.timeline.MatrixTimelineItem diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesTimelineItemState.kt b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/TimelineItem.kt similarity index 70% rename from features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesTimelineItemState.kt rename to features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/TimelineItem.kt index 6dab9e37ae..ef93d38aa2 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesTimelineItemState.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/TimelineItem.kt @@ -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. @@ -14,27 +14,30 @@ * limitations under the License. */ -package io.element.android.x.features.messages.model +package io.element.android.x.features.messages.timeline.model +import androidx.compose.runtime.Immutable import io.element.android.x.designsystem.components.avatar.AvatarData -import io.element.android.x.features.messages.model.content.MessagesTimelineItemContent +import io.element.android.x.features.messages.timeline.model.content.TimelineItemContent +import io.element.android.x.matrix.core.EventId -sealed interface MessagesTimelineItemState { +@Immutable +sealed interface TimelineItem { data class Virtual( val id: String - ) : MessagesTimelineItemState + ) : TimelineItem data class MessageEvent( - val id: String, + val id: EventId, val senderId: String, val senderDisplayName: String?, val senderAvatar: AvatarData, - val content: MessagesTimelineItemContent, + val content: TimelineItemContent, val sentTime: String = "", val isMine: Boolean = false, val groupPosition: MessagesItemGroupPosition = MessagesItemGroupPosition.None, - val reactionsState: MessagesItemReactionState - ) : MessagesTimelineItemState { + val reactionsState: TimelineItemReactions + ) : TimelineItem { val showSenderInformation = groupPosition.isNew() && !isMine diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesItemGroupPosition.kt b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/TimelineItemGroupPosition.kt similarity index 84% rename from features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesItemGroupPosition.kt rename to features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/TimelineItemGroupPosition.kt index b5385538a4..977c498bfc 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesItemGroupPosition.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/TimelineItemGroupPosition.kt @@ -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. @@ -14,10 +14,12 @@ * limitations under the License. */ -package io.element.android.x.features.messages.model +package io.element.android.x.features.messages.timeline.model +import androidx.compose.runtime.Immutable import androidx.compose.ui.tooling.preview.PreviewParameterProvider +@Immutable sealed interface MessagesItemGroupPosition { object First : MessagesItemGroupPosition object Middle : MessagesItemGroupPosition @@ -30,7 +32,7 @@ sealed interface MessagesItemGroupPosition { } } -internal class MessagesItemGroupPositionProvider : PreviewParameterProvider { +internal class TimelineItemGroupPositionProvider : PreviewParameterProvider { override val values = sequenceOf( MessagesItemGroupPosition.First, MessagesItemGroupPosition.Middle, diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesItemReactionState.kt b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/TimelineItemReactions.kt similarity index 74% rename from features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesItemReactionState.kt rename to features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/TimelineItemReactions.kt index b7cf57cf0a..ff0fd549bc 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesItemReactionState.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/TimelineItemReactions.kt @@ -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. @@ -14,16 +14,14 @@ * limitations under the License. */ -package io.element.android.x.features.messages.model +package io.element.android.x.features.messages.timeline.model -import androidx.compose.runtime.Stable +import kotlinx.collections.immutable.ImmutableList -@Stable -data class MessagesItemReactionState( - val reactions: List +data class TimelineItemReactions( + val reactions: ImmutableList ) -@Stable data class AggregatedReaction( val key: String, val count: String, diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemContent.kt b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/content/TimelineItemContent.kt similarity index 74% rename from features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemContent.kt rename to features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/content/TimelineItemContent.kt index fdfb2448d4..9d7b8c3868 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemContent.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/content/TimelineItemContent.kt @@ -14,32 +14,32 @@ * limitations under the License. */ -package io.element.android.x.features.messages.model.content +package io.element.android.x.features.messages.timeline.model.content import androidx.compose.ui.tooling.preview.PreviewParameterProvider import org.matrix.rustcomponents.sdk.EncryptedMessage -sealed interface MessagesTimelineItemContent +sealed interface TimelineItemContent -class MessagesTimelineItemContentProvider : PreviewParameterProvider { +class MessagesTimelineItemContentProvider : PreviewParameterProvider { override val values = sequenceOf( - MessagesTimelineItemEmoteContent( + TimelineItemEmoteContent( body = "Emote", htmlDocument = null ), - MessagesTimelineItemEncryptedContent( + TimelineItemEncryptedContent( encryptedMessage = EncryptedMessage.Unknown ), // TODO MessagesTimelineItemImageContent(), - MessagesTimelineItemNoticeContent( + TimelineItemNoticeContent( body = "Notice", htmlDocument = null ), - MessagesTimelineItemRedactedContent, - MessagesTimelineItemTextContent( + TimelineItemRedactedContent, + TimelineItemTextContent( body = "Text", htmlDocument = null ), - MessagesTimelineItemUnknownContent, + TimelineItemUnknownContent, ) } diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemTextContent.kt b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/content/TimelineItemEmoteContent.kt similarity index 82% rename from features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemTextContent.kt rename to features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/content/TimelineItemEmoteContent.kt index 6c4820dc51..c5c0a8a330 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemTextContent.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/content/TimelineItemEmoteContent.kt @@ -14,11 +14,11 @@ * limitations under the License. */ -package io.element.android.x.features.messages.model.content +package io.element.android.x.features.messages.timeline.model.content import org.jsoup.nodes.Document -data class MessagesTimelineItemTextContent( +data class TimelineItemEmoteContent( override val body: String, override val htmlDocument: Document? -) : MessagesTimelineItemTextBasedContent +) : TimelineItemTextBasedContent diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemEncryptedContent.kt b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/content/TimelineItemEncryptedContent.kt similarity index 83% rename from features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemEncryptedContent.kt rename to features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/content/TimelineItemEncryptedContent.kt index 0e23c0ab87..62fd231191 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemEncryptedContent.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/content/TimelineItemEncryptedContent.kt @@ -14,10 +14,10 @@ * limitations under the License. */ -package io.element.android.x.features.messages.model.content +package io.element.android.x.features.messages.timeline.model.content import org.matrix.rustcomponents.sdk.EncryptedMessage -data class MessagesTimelineItemEncryptedContent( +data class TimelineItemEncryptedContent( val encryptedMessage: EncryptedMessage -) : MessagesTimelineItemContent +) : TimelineItemContent diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemImageContent.kt b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/content/TimelineItemImageContent.kt similarity index 84% rename from features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemImageContent.kt rename to features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/content/TimelineItemImageContent.kt index 682c9838d2..25e983d6a4 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemImageContent.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/content/TimelineItemImageContent.kt @@ -14,13 +14,13 @@ * limitations under the License. */ -package io.element.android.x.features.messages.model.content +package io.element.android.x.features.messages.timeline.model.content import io.element.android.x.matrix.media.MediaResolver -data class MessagesTimelineItemImageContent( +data class TimelineItemImageContent( val body: String, val imageMeta: MediaResolver.Meta, val blurhash: String?, val aspectRatio: Float -) : MessagesTimelineItemContent +) : TimelineItemContent diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemEmoteContent.kt b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/content/TimelineItemNoticeContent.kt similarity index 82% rename from features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemEmoteContent.kt rename to features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/content/TimelineItemNoticeContent.kt index 2d13b93471..1bb8df5673 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemEmoteContent.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/content/TimelineItemNoticeContent.kt @@ -14,11 +14,11 @@ * limitations under the License. */ -package io.element.android.x.features.messages.model.content +package io.element.android.x.features.messages.timeline.model.content import org.jsoup.nodes.Document -data class MessagesTimelineItemEmoteContent( +data class TimelineItemNoticeContent( override val body: String, override val htmlDocument: Document? -) : MessagesTimelineItemTextBasedContent +) : TimelineItemTextBasedContent diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemUnknownContent.kt b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/content/TimelineItemRedactedContent.kt similarity index 81% rename from features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemUnknownContent.kt rename to features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/content/TimelineItemRedactedContent.kt index e46710c9fd..8de2088052 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemUnknownContent.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/content/TimelineItemRedactedContent.kt @@ -14,6 +14,6 @@ * limitations under the License. */ -package io.element.android.x.features.messages.model.content +package io.element.android.x.features.messages.timeline.model.content -object MessagesTimelineItemUnknownContent : MessagesTimelineItemContent +object TimelineItemRedactedContent : TimelineItemContent diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemTextBasedContent.kt b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/content/TimelineItemTextBasedContent.kt similarity index 82% rename from features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemTextBasedContent.kt rename to features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/content/TimelineItemTextBasedContent.kt index 14395a91fa..15f757bf6e 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemTextBasedContent.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/content/TimelineItemTextBasedContent.kt @@ -14,11 +14,11 @@ * limitations under the License. */ -package io.element.android.x.features.messages.model.content +package io.element.android.x.features.messages.timeline.model.content import org.jsoup.nodes.Document -sealed interface MessagesTimelineItemTextBasedContent : MessagesTimelineItemContent { +sealed interface TimelineItemTextBasedContent : TimelineItemContent { val body: String val htmlDocument: Document? } diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemNoticeContent.kt b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/content/TimelineItemTextContent.kt similarity index 82% rename from features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemNoticeContent.kt rename to features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/content/TimelineItemTextContent.kt index 8d85ba0c70..a3a887df66 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemNoticeContent.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/content/TimelineItemTextContent.kt @@ -14,11 +14,11 @@ * limitations under the License. */ -package io.element.android.x.features.messages.model.content +package io.element.android.x.features.messages.timeline.model.content import org.jsoup.nodes.Document -data class MessagesTimelineItemNoticeContent( +data class TimelineItemTextContent( override val body: String, override val htmlDocument: Document? -) : MessagesTimelineItemTextBasedContent +) : TimelineItemTextBasedContent diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemRedactedContent.kt b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/content/TimelineItemUnknownContent.kt similarity index 81% rename from features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemRedactedContent.kt rename to features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/content/TimelineItemUnknownContent.kt index 36f7a29d55..44f6cb9af4 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/model/content/MessagesTimelineItemRedactedContent.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/model/content/TimelineItemUnknownContent.kt @@ -14,6 +14,6 @@ * limitations under the License. */ -package io.element.android.x.features.messages.model.content +package io.element.android.x.features.messages.timeline.model.content -object MessagesTimelineItemRedactedContent : MessagesTimelineItemContent +object TimelineItemUnknownContent : TimelineItemContent diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/util/MutableListExt.kt b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/util/MutableListExt.kt similarity index 92% rename from features/messages/src/main/java/io/element/android/x/features/messages/util/MutableListExt.kt rename to features/messages/src/main/java/io/element/android/x/features/messages/timeline/util/MutableListExt.kt index 383cb04b95..d487af0cdc 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/util/MutableListExt.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/timeline/util/MutableListExt.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.x.features.messages.util +package io.element.android.x.features.messages.timeline.util internal inline fun MutableList.invalidateLast() { val indexOfLast = size diff --git a/libraries/matrix/src/main/java/io/element/android/x/matrix/Matrix.kt b/libraries/matrix/src/main/java/io/element/android/x/matrix/Matrix.kt index cd14368cb6..3c20fd677f 100644 --- a/libraries/matrix/src/main/java/io/element/android/x/matrix/Matrix.kt +++ b/libraries/matrix/src/main/java/io/element/android/x/matrix/Matrix.kt @@ -25,30 +25,23 @@ import io.element.android.x.matrix.core.SessionId import io.element.android.x.matrix.session.SessionStore import io.element.android.x.matrix.session.sessionId import io.element.android.x.matrix.util.logError -import java.io.File -import java.util.concurrent.Executors -import javax.inject.Inject import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.withContext import org.matrix.rustcomponents.sdk.AuthenticationService import org.matrix.rustcomponents.sdk.Client import org.matrix.rustcomponents.sdk.ClientBuilder import timber.log.Timber +import java.io.File +import javax.inject.Inject @SingleIn(AppScope::class) class Matrix @Inject constructor( private val coroutineScope: CoroutineScope, + private val coroutineDispatchers: CoroutineDispatchers, @ApplicationContext context: Context, ) { - private val coroutineDispatchers = CoroutineDispatchers( - io = Dispatchers.IO, - computation = Dispatchers.Default, - main = Dispatchers.Main, - diffUpdateDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher() - ) + private val baseDirectory = File(context.filesDir, "sessions") private val sessionStore = SessionStore(context) private val authService = AuthenticationService(baseDirectory.absolutePath) @@ -57,7 +50,7 @@ class Matrix @Inject constructor( return sessionStore.isLoggedIn() } - suspend fun getLatestSessionId(): SessionId? = withContext(coroutineDispatchers.io){ + suspend fun getLatestSessionId(): SessionId? = withContext(coroutineDispatchers.io) { sessionStore.getLatestSession()?.sessionId() } diff --git a/libraries/matrix/src/main/java/io/element/android/x/matrix/room/MatrixRoom.kt b/libraries/matrix/src/main/java/io/element/android/x/matrix/room/MatrixRoom.kt index df86749a15..681b8839ee 100644 --- a/libraries/matrix/src/main/java/io/element/android/x/matrix/room/MatrixRoom.kt +++ b/libraries/matrix/src/main/java/io/element/android/x/matrix/room/MatrixRoom.kt @@ -17,6 +17,7 @@ package io.element.android.x.matrix.room import io.element.android.x.core.coroutine.CoroutineDispatchers +import io.element.android.x.matrix.core.EventId import io.element.android.x.matrix.core.RoomId import io.element.android.x.matrix.timeline.MatrixTimeline import kotlinx.coroutines.CoroutineScope @@ -39,13 +40,15 @@ class MatrixRoom( private val coroutineDispatchers: CoroutineDispatchers, ) { - fun syncUpdateFlow(): Flow { + fun syncUpdateFlow(): Flow { return slidingSyncUpdateFlow .filter { it.rooms.contains(room.id()) } - .map { } - .onStart { emit(Unit) } + .map { + System.currentTimeMillis() + } + .onStart { emit(System.currentTimeMillis()) } } fun timeline(): MatrixTimeline { @@ -107,26 +110,26 @@ class MatrixRoom( } } - suspend fun editMessage(originalEventId: String, message: String): Result = withContext(coroutineDispatchers.io) { + suspend fun editMessage(originalEventId: EventId, message: String): Result = withContext(coroutineDispatchers.io) { val transactionId = genTransactionId() // val content = messageEventContentFromMarkdown(message) runCatching { - room.edit(/* TODO use content */ message, originalEventId, transactionId) + room.edit(/* TODO use content */ message, originalEventId.value, transactionId) } } - suspend fun replyMessage(eventId: String, message: String): Result = withContext(coroutineDispatchers.io) { + suspend fun replyMessage(eventId: EventId, message: String): Result = withContext(coroutineDispatchers.io) { val transactionId = genTransactionId() // val content = messageEventContentFromMarkdown(message) runCatching { - room.sendReply(/* TODO use content */ message, eventId, transactionId) + room.sendReply(/* TODO use content */ message, eventId.value, transactionId) } } - suspend fun redactEvent(eventId: String, reason: String? = null) = withContext(coroutineDispatchers.io) { + suspend fun redactEvent(eventId: EventId, reason: String? = null) = withContext(coroutineDispatchers.io) { val transactionId = genTransactionId() runCatching { - room.redact(eventId, reason, transactionId) + room.redact(eventId.value, reason, transactionId) } } } diff --git a/libraries/matrix/src/main/java/io/element/android/x/matrix/timeline/MatrixTimeline.kt b/libraries/matrix/src/main/java/io/element/android/x/matrix/timeline/MatrixTimeline.kt index a302574b8e..bf613ca761 100644 --- a/libraries/matrix/src/main/java/io/element/android/x/matrix/timeline/MatrixTimeline.kt +++ b/libraries/matrix/src/main/java/io/element/android/x/matrix/timeline/MatrixTimeline.kt @@ -17,6 +17,7 @@ package io.element.android.x.matrix.timeline import io.element.android.x.core.coroutine.CoroutineDispatchers +import io.element.android.x.matrix.core.EventId import io.element.android.x.matrix.room.MatrixRoom import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.FlowPreview @@ -146,11 +147,11 @@ class MatrixTimeline( return matrixRoom.sendMessage(message) } - suspend fun editMessage(originalEventId: String, message: String): Result { + suspend fun editMessage(originalEventId: EventId, message: String): Result { return matrixRoom.editMessage(originalEventId, message = message) } - suspend fun replyMessage(inReplyToEventId: String, message: String): Result { + suspend fun replyMessage(inReplyToEventId: EventId, message: String): Result { return matrixRoom.replyMessage(inReplyToEventId, message) } diff --git a/libraries/matrixui/src/main/java/io/element/android/x/matrix/ui/MatrixItemHelper.kt b/libraries/matrixui/src/main/java/io/element/android/x/matrix/ui/MatrixItemHelper.kt index a040e18585..49a97b3a5f 100644 --- a/libraries/matrixui/src/main/java/io/element/android/x/matrix/ui/MatrixItemHelper.kt +++ b/libraries/matrixui/src/main/java/io/element/android/x/matrix/ui/MatrixItemHelper.kt @@ -26,8 +26,9 @@ import io.element.android.x.matrix.ui.model.MatrixUser import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.asFlow +import javax.inject.Inject -class MatrixItemHelper( +class MatrixItemHelper @Inject constructor( private val client: MatrixClient ) { /** diff --git a/libraries/textcomposer/build.gradle.kts b/libraries/textcomposer/build.gradle.kts index e5d8942511..cda96fb319 100644 --- a/libraries/textcomposer/build.gradle.kts +++ b/libraries/textcomposer/build.gradle.kts @@ -32,6 +32,7 @@ android { dependencies { implementation(project(":libraries:elementresources")) implementation(project(":libraries:core")) + implementation(project(":libraries:matrix")) implementation(libs.wysiwyg) implementation(libs.androidx.constraintlayout) implementation("com.google.android.material:material:1.7.0") diff --git a/libraries/textcomposer/src/main/java/io/element/android/x/textcomposer/MessageComposerMode.kt b/libraries/textcomposer/src/main/java/io/element/android/x/textcomposer/MessageComposerMode.kt index 224409829c..715b9c6cd6 100644 --- a/libraries/textcomposer/src/main/java/io/element/android/x/textcomposer/MessageComposerMode.kt +++ b/libraries/textcomposer/src/main/java/io/element/android/x/textcomposer/MessageComposerMode.kt @@ -17,31 +17,32 @@ package io.element.android.x.textcomposer import android.os.Parcelable +import io.element.android.x.matrix.core.EventId import kotlinx.parcelize.Parcelize sealed interface MessageComposerMode : Parcelable { @Parcelize data class Normal(val content: CharSequence?) : MessageComposerMode - sealed class Special(open val eventId: String, open val defaultContent: CharSequence) : + sealed class Special(open val eventId: EventId, open val defaultContent: CharSequence) : MessageComposerMode @Parcelize - data class Edit(override val eventId: String, override val defaultContent: CharSequence) : + data class Edit(override val eventId: EventId, override val defaultContent: CharSequence) : Special(eventId, defaultContent) @Parcelize - class Quote(override val eventId: String, override val defaultContent: CharSequence) : + class Quote(override val eventId: EventId, override val defaultContent: CharSequence) : Special(eventId, defaultContent) @Parcelize class Reply( val senderName: String, - override val eventId: String, + override val eventId: EventId, override val defaultContent: CharSequence ) : Special(eventId, defaultContent) - val relatedEventId: String? + val relatedEventId: EventId? get() = when (this) { is Normal -> null is Edit -> eventId