diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNavigator.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNavigator.kt index 3eba997eea..470d9da9c4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNavigator.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNavigator.kt @@ -7,13 +7,16 @@ package io.element.android.features.messages.impl +import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo +import kotlinx.collections.immutable.ImmutableList interface MessagesNavigator { fun onShowEventDebugInfoClick(eventId: EventId?, debugInfo: TimelineItemDebugInfo) fun onForwardEventClick(eventId: EventId) fun onReportContentClick(eventId: EventId, senderId: UserId) fun onEditPollClick(eventId: EventId) + fun onPreviewAttachment(attachments: ImmutableList) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt index 9fd823445f..7d0d6cb2d0 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt @@ -30,6 +30,7 @@ import io.element.android.anvilannotations.ContributesNode import io.element.android.compound.theme.ElementTheme import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.features.messages.impl.messagecomposer.MessageComposerEvents +import io.element.android.features.messages.impl.messagecomposer.MessageComposerPresenter import io.element.android.features.messages.impl.timeline.TimelineEvents import io.element.android.features.messages.impl.timeline.di.LocalTimelineItemPresenterFactories import io.element.android.features.messages.impl.timeline.di.TimelineItemPresenterFactories @@ -60,12 +61,16 @@ class MessagesNode @AssistedInject constructor( @Assisted plugins: List, private val room: MatrixRoom, private val analyticsService: AnalyticsService, + messageComposerPresenterFactory: MessageComposerPresenter.Factory, presenterFactory: MessagesPresenter.Factory, private val timelineItemPresenterFactories: TimelineItemPresenterFactories, private val mediaPlayer: MediaPlayer, private val permalinkParser: PermalinkParser, ) : Node(buildContext, plugins = plugins), MessagesNavigator { - private val presenter = presenterFactory.create(this) + private val presenter = presenterFactory.create( + navigator = this, + composerPresenter = messageComposerPresenterFactory.create(this), + ) private val callbacks = plugins() data class Inputs(val focusedEventId: EventId?) : NodeInputs @@ -114,10 +119,6 @@ class MessagesNode @AssistedInject constructor( .orFalse() } - private fun onPreviewAttachments(attachments: ImmutableList) { - callbacks.forEach { it.onPreviewAttachments(attachments) } - } - private fun onUserDataClick(userId: UserId) { callbacks.forEach { it.onUserDataClick(userId) } } @@ -178,6 +179,10 @@ class MessagesNode @AssistedInject constructor( callbacks.forEach { it.onEditPollClick(eventId) } } + override fun onPreviewAttachment(attachments: ImmutableList) { + callbacks.forEach { it.onPreviewAttachments(attachments) } + } + private fun onViewAllPinnedMessagesClick() { callbacks.forEach { it.onViewAllPinnedEvents() } } @@ -213,7 +218,6 @@ class MessagesNode @AssistedInject constructor( onBackClick = this::navigateUp, onRoomDetailsClick = this::onRoomDetailsClick, onEventContentClick = this::onEventClick, - onPreviewAttachments = this::onPreviewAttachments, onUserDataClick = this::onUserDataClick, onLinkClick = { url -> onLinkClick(activity, isDark, url, state.timelineState.eventSink) }, onSendLocationClick = this::onSendLocationClick, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt index 49d3015c05..8f11c491e2 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt @@ -89,12 +89,12 @@ import timber.log.Timber class MessagesPresenter @AssistedInject constructor( @Assisted private val navigator: MessagesNavigator, private val room: MatrixRoom, - private val composerPresenter: Presenter, + @Assisted private val composerPresenter: Presenter, private val voiceMessageComposerPresenter: Presenter, timelinePresenterFactory: TimelinePresenter.Factory, private val timelineProtectionPresenter: Presenter, private val identityChangeStatePresenter: Presenter, - private val actionListPresenterFactory: ActionListPresenter.Factory, + actionListPresenterFactory: ActionListPresenter.Factory, private val customReactionPresenter: Presenter, private val reactionSummaryPresenter: Presenter, private val readReceiptBottomSheetPresenter: Presenter, @@ -116,7 +116,10 @@ class MessagesPresenter @AssistedInject constructor( @AssistedFactory interface Factory { - fun create(navigator: MessagesNavigator): MessagesPresenter + fun create( + navigator: MessagesNavigator, + composerPresenter: Presenter, + ): MessagesPresenter } @Composable diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index 49cbac6268..fa66e463bb 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -32,10 +32,8 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -55,10 +53,8 @@ import io.element.android.compound.theme.ElementTheme import io.element.android.features.messages.impl.actionlist.ActionListEvents import io.element.android.features.messages.impl.actionlist.ActionListView import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction -import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.features.messages.impl.crypto.identity.IdentityChangeStateView import io.element.android.features.messages.impl.messagecomposer.AttachmentsBottomSheet -import io.element.android.features.messages.impl.messagecomposer.AttachmentsState import io.element.android.features.messages.impl.messagecomposer.MessageComposerEvents import io.element.android.features.messages.impl.messagecomposer.MessageComposerView import io.element.android.features.messages.impl.messagecomposer.suggestions.SuggestionsPickerView @@ -115,7 +111,6 @@ fun MessagesView( onEventContentClick: (event: TimelineItem.Event) -> Boolean, onUserDataClick: (UserId) -> Unit, onLinkClick: (String) -> Unit, - onPreviewAttachments: (ImmutableList) -> Unit, onSendLocationClick: () -> Unit, onCreatePollClick: () -> Unit, onJoinCallClick: () -> Unit, @@ -129,11 +124,6 @@ fun MessagesView( KeepScreenOn(state.voiceMessageComposerState.keepScreenOn) - AttachmentStateView( - state = state.composerState.attachmentsState, - onPreviewAttachments = onPreviewAttachments, - ) - val snackbarHostState = rememberSnackbarHostState(snackbarMessage = state.snackbarMessage) // This is needed because the composer is inside an AndroidView that can't be affected by the FocusManager in Compose @@ -273,22 +263,6 @@ private fun ReinviteDialog(state: MessagesState) { } } -@Composable -private fun AttachmentStateView( - state: AttachmentsState, - onPreviewAttachments: (ImmutableList) -> Unit, -) { - when (state) { - AttachmentsState.None -> Unit - is AttachmentsState.Previewing -> { - val latestOnPreviewAttachments by rememberUpdatedState(onPreviewAttachments) - LaunchedEffect(state) { - latestOnPreviewAttachments(state.attachments) - } - } - } -} - @Composable private fun MessagesViewContent( state: MessagesState, @@ -557,7 +531,6 @@ internal fun MessagesViewPreview(@PreviewParameter(MessagesStateProvider::class) onEventContentClick = { false }, onUserDataClick = {}, onLinkClick = {}, - onPreviewAttachments = {}, onSendLocationClick = {}, onCreatePollClick = {}, onJoinCallClick = {}, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/MessagesViewWithIdentityChangePreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/MessagesViewWithIdentityChangePreview.kt index 04750b6ad8..34c58bdb06 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/MessagesViewWithIdentityChangePreview.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/MessagesViewWithIdentityChangePreview.kt @@ -36,7 +36,6 @@ internal fun MessagesViewWithIdentityChangePreview( onEventContentClick = { false }, onUserDataClick = {}, onLinkClick = {}, - onPreviewAttachments = {}, onSendLocationClick = {}, onCreatePollClick = {}, onJoinCallClick = {}, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/di/MessagesModule.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/di/MessagesModule.kt index d987e97809..567c95bcc7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/di/MessagesModule.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/di/MessagesModule.kt @@ -14,8 +14,6 @@ import io.element.android.features.messages.impl.crypto.identity.IdentityChangeS import io.element.android.features.messages.impl.crypto.identity.IdentityChangeStatePresenter import io.element.android.features.messages.impl.crypto.sendfailure.resolve.ResolveVerifiedUserSendFailurePresenter import io.element.android.features.messages.impl.crypto.sendfailure.resolve.ResolveVerifiedUserSendFailureState -import io.element.android.features.messages.impl.messagecomposer.MessageComposerPresenter -import io.element.android.features.messages.impl.messagecomposer.MessageComposerState import io.element.android.features.messages.impl.pinned.banner.PinnedMessagesBannerPresenter import io.element.android.features.messages.impl.pinned.banner.PinnedMessagesBannerState import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionPresenter @@ -48,9 +46,6 @@ interface MessagesModule { @Binds fun bindTimelineProtectionPresenter(presenter: TimelineProtectionPresenter): Presenter - @Binds - fun bindMessageComposerPresenter(presenter: MessageComposerPresenter): Presenter - @Binds fun bindVoiceMessageComposerPresenter(presenter: VoiceMessageComposerPresenter): Presenter diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt index ebccc8dff4..bf3ea2b112 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt @@ -14,7 +14,6 @@ import androidx.annotation.VisibleForTesting import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.MutableState import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateListOf @@ -26,8 +25,12 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.media3.common.MimeTypes import androidx.media3.common.util.UnstableApi +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.features.analytics.plan.Composer import im.vector.app.features.analytics.plan.Interaction +import io.element.android.features.messages.impl.MessagesNavigator import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.features.messages.impl.attachments.preview.error.sendAttachmentError import io.element.android.features.messages.impl.draft.ComposerDraftService @@ -38,8 +41,6 @@ import io.element.android.features.messages.impl.utils.TextPillificationHelper import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage -import io.element.android.libraries.di.RoomScope -import io.element.android.libraries.di.SingleIn import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.core.UserId @@ -89,12 +90,11 @@ import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.merge import kotlinx.coroutines.launch import timber.log.Timber -import javax.inject.Inject import kotlin.time.Duration.Companion.seconds import io.element.android.libraries.core.mimetype.MimeTypes.Any as AnyMimeTypes -@SingleIn(RoomScope::class) -class MessageComposerPresenter @Inject constructor( +class MessageComposerPresenter @AssistedInject constructor( + @Assisted private val navigator: MessagesNavigator, private val appCoroutineScope: CoroutineScope, private val room: MatrixRoom, private val mediaPickerProvider: PickerProvider, @@ -117,6 +117,11 @@ class MessageComposerPresenter @Inject constructor( private val roomMemberProfilesCache: RoomMemberProfilesCache, private val suggestionsProcessor: SuggestionsProcessor, ) : Presenter { + @AssistedFactory + interface Factory { + fun create(navigator: MessagesNavigator): MessageComposerPresenter + } + private val cameraPermissionPresenter = permissionsPresenterFactory.create(Manifest.permission.CAMERA) private var pendingEvent: MessageComposerEvents? = null private val suggestionSearchTrigger = MutableStateFlow(null) @@ -147,9 +152,6 @@ class MessageComposerPresenter @Inject constructor( } val cameraPermissionState = cameraPermissionPresenter.present() - val attachmentsState = remember { - mutableStateOf(AttachmentsState.None) - } val canShareLocation = remember { mutableStateOf(false) } LaunchedEffect(Unit) { @@ -162,16 +164,16 @@ class MessageComposerPresenter @Inject constructor( } val galleryMediaPicker = mediaPickerProvider.registerGalleryPicker { uri, mimeType -> - handlePickedMedia(attachmentsState, uri, mimeType) + handlePickedMedia(uri, mimeType) } val filesPicker = mediaPickerProvider.registerFilePicker(AnyMimeTypes) { uri -> - handlePickedMedia(attachmentsState, uri) + handlePickedMedia(uri) } val cameraPhotoPicker = mediaPickerProvider.registerCameraPhotoPicker { uri -> - handlePickedMedia(attachmentsState, uri, MimeTypes.IMAGE_JPEG) + handlePickedMedia(uri, MimeTypes.IMAGE_JPEG) } val cameraVideoPicker = mediaPickerProvider.registerCameraVideoPicker { uri -> - handlePickedMedia(attachmentsState, uri, MimeTypes.VIDEO_MP4) + handlePickedMedia(uri, MimeTypes.VIDEO_MP4) } val isFullScreen = rememberSaveable { mutableStateOf(false) @@ -277,7 +279,6 @@ class MessageComposerPresenter @Inject constructor( formattedFileSize = null ), ), - attachmentState = attachmentsState, ) is MessageComposerEvents.SetMode -> { localCoroutineScope.setMode(event.composerMode, markdownTextEditorState, richTextEditorState) @@ -396,7 +397,6 @@ class MessageComposerPresenter @Inject constructor( showTextFormatting = showTextFormatting, canShareLocation = canShareLocation.value, canCreatePoll = canCreatePoll.value, - attachmentsState = attachmentsState.value, suggestions = suggestions.toPersistentList(), resolveMentionDisplay = resolveMentionDisplay, eventSink = { handleEvents(it) }, @@ -459,14 +459,12 @@ class MessageComposerPresenter @Inject constructor( private fun CoroutineScope.sendAttachment( attachment: Attachment, - attachmentState: MutableState, ) = when (attachment) { is Attachment.Media -> { launch { sendMedia( uri = attachment.localMedia.uri, mimeType = attachment.localMedia.info.mimeType, - attachmentState = attachmentState, ) } } @@ -474,14 +472,10 @@ class MessageComposerPresenter @Inject constructor( @UnstableApi private fun handlePickedMedia( - attachmentsState: MutableState, uri: Uri?, mimeType: String? = null, ) { - if (uri == null) { - attachmentsState.value = AttachmentsState.None - return - } + uri ?: return val localMedia = localMediaFactory.createFromUri( uri = uri, mimeType = mimeType, @@ -489,13 +483,12 @@ class MessageComposerPresenter @Inject constructor( formattedFileSize = null ) val mediaAttachment = Attachment.Media(localMedia) - attachmentsState.value = AttachmentsState.Previewing(persistentListOf(mediaAttachment)) + navigator.onPreviewAttachment(persistentListOf(mediaAttachment)) } private suspend fun sendMedia( uri: Uri, mimeType: String, - attachmentState: MutableState, ) = runCatching { mediaSender.sendMedia( uri = uri, @@ -503,12 +496,8 @@ class MessageComposerPresenter @Inject constructor( progressCallback = null, ).getOrThrow() } - .onSuccess { - attachmentState.value = AttachmentsState.None - } .onFailure { cause -> Timber.e(cause, "Failed to send attachment") - attachmentState.value = AttachmentsState.None if (cause is CancellationException) { throw cause } else { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerState.kt index d0e9528e48..58e40dbb14 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerState.kt @@ -7,9 +7,7 @@ package io.element.android.features.messages.impl.messagecomposer -import androidx.compose.runtime.Immutable import androidx.compose.runtime.Stable -import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.libraries.textcomposer.mentions.ResolvedSuggestion import io.element.android.libraries.textcomposer.model.MessageComposerMode import io.element.android.libraries.textcomposer.model.TextEditorState @@ -25,14 +23,7 @@ data class MessageComposerState( val showTextFormatting: Boolean, val canShareLocation: Boolean, val canCreatePoll: Boolean, - val attachmentsState: AttachmentsState, val suggestions: ImmutableList, val resolveMentionDisplay: (String, String) -> TextDisplay, val eventSink: (MessageComposerEvents) -> Unit, ) - -@Immutable -sealed interface AttachmentsState { - data object None : AttachmentsState - data class Previewing(val attachments: ImmutableList) : AttachmentsState -} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt index a36102bc7d..7811322c3d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt @@ -31,7 +31,6 @@ fun aMessageComposerState( showAttachmentSourcePicker: Boolean = false, canShareLocation: Boolean = true, canCreatePoll: Boolean = true, - attachmentsState: AttachmentsState = AttachmentsState.None, suggestions: ImmutableList = persistentListOf(), eventSink: (MessageComposerEvents) -> Unit = {}, ) = MessageComposerState( @@ -42,7 +41,6 @@ fun aMessageComposerState( showAttachmentSourcePicker = showAttachmentSourcePicker, canShareLocation = canShareLocation, canCreatePoll = canCreatePoll, - attachmentsState = attachmentsState, suggestions = suggestions, resolveMentionDisplay = { _, _ -> TextDisplay.Plain }, eventSink = eventSink, diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/FakeMessagesNavigator.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/FakeMessagesNavigator.kt index fa5e412f27..d11398bd8e 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/FakeMessagesNavigator.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/FakeMessagesNavigator.kt @@ -7,9 +7,11 @@ package io.element.android.features.messages.impl +import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo +import kotlinx.collections.immutable.ImmutableList class FakeMessagesNavigator : MessagesNavigator { var onShowEventDebugInfoClickedCount = 0 @@ -24,6 +26,9 @@ class FakeMessagesNavigator : MessagesNavigator { var onEditPollClickedCount = 0 private set + var onPreviewAttachmentCount = 0 + private set + override fun onShowEventDebugInfoClick(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { onShowEventDebugInfoClickedCount++ } @@ -39,4 +44,8 @@ class FakeMessagesNavigator : MessagesNavigator { override fun onEditPollClick(eventId: EventId) { onEditPollClickedCount++ } + + override fun onPreviewAttachment(attachments: ImmutableList) { + onPreviewAttachmentCount++ + } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt index c040ce9926..35c069e1f9 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt @@ -514,7 +514,6 @@ private fun AndroidComposeTestRule.setMessa onEventClick: (event: TimelineItem.Event) -> Boolean = EnsureNeverCalledWithParamAndResult(), onUserDataClick: (UserId) -> Unit = EnsureNeverCalledWithParam(), onLinkClick: (String) -> Unit = EnsureNeverCalledWithParam(), - onPreviewAttachments: (ImmutableList) -> Unit = EnsureNeverCalledWithParam(), onSendLocationClick: () -> Unit = EnsureNeverCalled(), onCreatePollClick: () -> Unit = EnsureNeverCalled(), onJoinCallClick: () -> Unit = EnsureNeverCalled(), @@ -532,7 +531,6 @@ private fun AndroidComposeTestRule.setMessa onEventContentClick = onEventClick, onUserDataClick = onUserDataClick, onLinkClick = onLinkClick, - onPreviewAttachments = onPreviewAttachments, onSendLocationClick = onSendLocationClick, onCreatePollClick = onCreatePollClick, onJoinCallClick = onJoinCallClick, diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterTest.kt index 1654a061c8..fcd9102be6 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterTest.kt @@ -18,6 +18,8 @@ import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.Composer import im.vector.app.features.analytics.plan.Interaction +import io.element.android.features.messages.impl.FakeMessagesNavigator +import io.element.android.features.messages.impl.MessagesNavigator import io.element.android.features.messages.impl.draft.ComposerDraftService import io.element.android.features.messages.impl.draft.FakeComposerDraftService import io.element.android.features.messages.impl.messagecomposer.suggestions.SuggestionsProcessor @@ -133,7 +135,6 @@ class MessageComposerPresenterTest { assertThat(initialState.mode).isEqualTo(MessageComposerMode.Normal) assertThat(initialState.showAttachmentSourcePicker).isFalse() assertThat(initialState.canShareLocation).isTrue() - assertThat(initialState.attachmentsState).isEqualTo(AttachmentsState.None) } } @@ -685,7 +686,12 @@ class MessageComposerPresenterTest { val room = FakeMatrixRoom( typingNoticeResult = { Result.success(Unit) } ) - val presenter = createPresenter(this, room = room) + val navigator = FakeMessagesNavigator() + val presenter = createPresenter( + coroutineScope = this, + room = room, + navigator = navigator, + ) pickerProvider.givenMimeType(MimeTypes.Images) mediaPreProcessor.givenResult( Result.success( @@ -709,9 +715,7 @@ class MessageComposerPresenterTest { }.test { val initialState = awaitFirstItem() initialState.eventSink(MessageComposerEvents.PickAttachmentSource.FromGallery) - val previewingState = awaitItem() - assertThat(previewingState.showAttachmentSourcePicker).isFalse() - assertThat(previewingState.attachmentsState).isInstanceOf(AttachmentsState.Previewing::class.java) + assertThat(navigator.onPreviewAttachmentCount).isEqualTo(1) } } @@ -720,7 +724,12 @@ class MessageComposerPresenterTest { val room = FakeMatrixRoom( typingNoticeResult = { Result.success(Unit) } ) - val presenter = createPresenter(this, room = room) + val navigator = FakeMessagesNavigator() + val presenter = createPresenter( + coroutineScope = this, + room = room, + navigator = navigator, + ) pickerProvider.givenMimeType(MimeTypes.Videos) mediaPreProcessor.givenResult( Result.success( @@ -745,9 +754,7 @@ class MessageComposerPresenterTest { }.test { val initialState = awaitFirstItem() initialState.eventSink(MessageComposerEvents.PickAttachmentSource.FromGallery) - val previewingState = awaitItem() - assertThat(previewingState.showAttachmentSourcePicker).isFalse() - assertThat(previewingState.attachmentsState).isInstanceOf(AttachmentsState.Previewing::class.java) + assertThat(navigator.onPreviewAttachmentCount).isEqualTo(1) } } @@ -772,15 +779,18 @@ class MessageComposerPresenterTest { val room = FakeMatrixRoom( typingNoticeResult = { Result.success(Unit) } ) - val presenter = createPresenter(this, room = room) + val navigator = FakeMessagesNavigator() + val presenter = createPresenter( + coroutineScope = this, + room = room, + navigator = navigator, + ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitFirstItem() initialState.eventSink(MessageComposerEvents.PickAttachmentSource.FromFiles) - val sendingState = awaitItem() - assertThat(sendingState.showAttachmentSourcePicker).isFalse() - assertThat(sendingState.attachmentsState).isInstanceOf(AttachmentsState.Previewing::class.java) + assertThat(navigator.onPreviewAttachmentCount).isEqualTo(1) } } @@ -828,19 +838,19 @@ class MessageComposerPresenterTest { typingNoticeResult = { Result.success(Unit) } ) val permissionPresenter = FakePermissionsPresenter().apply { setPermissionGranted() } + val navigator = FakeMessagesNavigator() val presenter = createPresenter( - this, + coroutineScope = this, room = room, permissionPresenter = permissionPresenter, + navigator = navigator, ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitFirstItem() initialState.eventSink(MessageComposerEvents.PickAttachmentSource.PhotoFromCamera) - val finalState = awaitItem() - assertThat(finalState.attachmentsState).isInstanceOf(AttachmentsState.Previewing::class.java) - cancelAndIgnoreRemainingEvents() + assertThat(navigator.onPreviewAttachmentCount).isEqualTo(1) } } @@ -850,23 +860,20 @@ class MessageComposerPresenterTest { typingNoticeResult = { Result.success(Unit) } ) val permissionPresenter = FakePermissionsPresenter() + val navigator = FakeMessagesNavigator() val presenter = createPresenter( - this, + coroutineScope = this, room = room, permissionPresenter = permissionPresenter, + navigator = navigator, ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitFirstItem() initialState.eventSink(MessageComposerEvents.PickAttachmentSource.PhotoFromCamera) - val permissionState = awaitItem() - assertThat(permissionState.showAttachmentSourcePicker).isFalse() - assertThat(permissionState.attachmentsState).isInstanceOf(AttachmentsState.None::class.java) permissionPresenter.setPermissionGranted() - skipItems(1) - val finalState = awaitItem() - assertThat(finalState.attachmentsState).isInstanceOf(AttachmentsState.Previewing::class.java) + assertThat(navigator.onPreviewAttachmentCount).isEqualTo(1) cancelAndIgnoreRemainingEvents() } } @@ -877,19 +884,19 @@ class MessageComposerPresenterTest { typingNoticeResult = { Result.success(Unit) } ) val permissionPresenter = FakePermissionsPresenter().apply { setPermissionGranted() } + val navigator = FakeMessagesNavigator() val presenter = createPresenter( - this, + coroutineScope = this, room = room, permissionPresenter = permissionPresenter, + navigator = navigator, ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitFirstItem() initialState.eventSink(MessageComposerEvents.PickAttachmentSource.VideoFromCamera) - val finalState = awaitItem() - assertThat(finalState.attachmentsState).isInstanceOf(AttachmentsState.Previewing::class.java) - cancelAndIgnoreRemainingEvents() + assertThat(navigator.onPreviewAttachmentCount).isEqualTo(1) } } @@ -899,10 +906,12 @@ class MessageComposerPresenterTest { typingNoticeResult = { Result.success(Unit) } ) val permissionPresenter = FakePermissionsPresenter() + val navigator = FakeMessagesNavigator() val presenter = createPresenter( - this, + coroutineScope = this, room = room, permissionPresenter = permissionPresenter, + navigator = navigator, ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -911,12 +920,9 @@ class MessageComposerPresenterTest { initialState.eventSink(MessageComposerEvents.PickAttachmentSource.VideoFromCamera) val permissionState = awaitItem() assertThat(permissionState.showAttachmentSourcePicker).isFalse() - assertThat(permissionState.attachmentsState).isInstanceOf(AttachmentsState.None::class.java) permissionPresenter.setPermissionGranted() skipItems(1) - val finalState = awaitItem() - assertThat(finalState.attachmentsState).isInstanceOf(AttachmentsState.Previewing::class.java) - cancelAndIgnoreRemainingEvents() + assertThat(navigator.onPreviewAttachmentCount).isEqualTo(1) } } @@ -1500,6 +1506,7 @@ class MessageComposerPresenterTest { room: MatrixRoom = FakeMatrixRoom( typingNoticeResult = { Result.success(Unit) } ), + navigator: MessagesNavigator = FakeMessagesNavigator(), pickerProvider: PickerProvider = this.pickerProvider, featureFlagService: FeatureFlagService = this.featureFlagService, sessionPreferencesStore: SessionPreferencesStore = InMemorySessionPreferencesStore(), @@ -1514,6 +1521,7 @@ class MessageComposerPresenterTest { isRichTextEditorEnabled: Boolean = true, draftService: ComposerDraftService = FakeComposerDraftService(), ) = MessageComposerPresenter( + navigator = navigator, appCoroutineScope = coroutineScope, room = room, mediaPickerProvider = pickerProvider,