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..1798584853 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,7 +30,9 @@ 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.TimelinePresenter import io.element.android.features.messages.impl.timeline.di.LocalTimelineItemPresenterFactories import io.element.android.features.messages.impl.timeline.di.TimelineItemPresenterFactories import io.element.android.features.messages.impl.timeline.model.TimelineItem @@ -60,12 +62,18 @@ class MessagesNode @AssistedInject constructor( @Assisted plugins: List, private val room: MatrixRoom, private val analyticsService: AnalyticsService, + messageComposerPresenterFactory: MessageComposerPresenter.Factory, + timelinePresenterFactory: TimelinePresenter.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), + timelinePresenter = timelinePresenterFactory.create(this), + ) private val callbacks = plugins() data class Inputs(val focusedEventId: EventId?) : NodeInputs @@ -114,10 +122,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 +182,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 +221,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..e39056ec9b 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 @@ -37,7 +37,6 @@ import io.element.android.features.messages.impl.messagecomposer.MessageComposer import io.element.android.features.messages.impl.pinned.banner.PinnedMessagesBannerState import io.element.android.features.messages.impl.timeline.TimelineController import io.element.android.features.messages.impl.timeline.TimelineEvents -import io.element.android.features.messages.impl.timeline.TimelinePresenter import io.element.android.features.messages.impl.timeline.TimelineState import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionState import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryState @@ -89,12 +88,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, + @Assisted private val timelinePresenter: Presenter, 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, @@ -111,12 +110,15 @@ class MessagesPresenter @AssistedInject constructor( private val permalinkParser: PermalinkParser, private val analyticsService: AnalyticsService, ) : Presenter { - private val timelinePresenter = timelinePresenterFactory.create(navigator = navigator) private val actionListPresenter = actionListPresenterFactory.create(TimelineItemActionPostProcessor.Default) @AssistedFactory interface Factory { - fun create(navigator: MessagesNavigator): MessagesPresenter + fun create( + navigator: MessagesNavigator, + composerPresenter: Presenter, + timelinePresenter: 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/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenter.kt index cf8d5668a3..cc6d134802 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenter.kt @@ -23,8 +23,6 @@ import im.vector.app.features.analytics.plan.Composer import io.element.android.features.messages.api.MessageComposerContext import io.element.android.features.messages.impl.voicemessages.VoiceMessageException import io.element.android.libraries.architecture.Presenter -import io.element.android.libraries.di.RoomScope -import io.element.android.libraries.di.SingleIn import io.element.android.libraries.mediaupload.api.MediaSender import io.element.android.libraries.permissions.api.PermissionsEvents import io.element.android.libraries.permissions.api.PermissionsPresenter @@ -45,7 +43,6 @@ import javax.inject.Inject import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds -@SingleIn(RoomScope::class) class VoiceMessageComposerPresenter @Inject constructor( private val appCoroutineScope: CoroutineScope, private val voiceRecorder: VoiceRecorder, 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..5dcc4e7496 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,36 +7,37 @@ 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 - -class FakeMessagesNavigator : MessagesNavigator { - var onShowEventDebugInfoClickedCount = 0 - private set - - var onForwardEventClickedCount = 0 - private set - - var onReportContentClickedCount = 0 - private set - - var onEditPollClickedCount = 0 - private set - +import io.element.android.tests.testutils.lambda.lambdaError +import kotlinx.collections.immutable.ImmutableList + +class FakeMessagesNavigator( + private val onShowEventDebugInfoClickLambda: (eventId: EventId?, debugInfo: TimelineItemDebugInfo) -> Unit = { _, _ -> lambdaError() }, + private val onForwardEventClickLambda: (eventId: EventId) -> Unit = { _ -> lambdaError() }, + private val onReportContentClickLambda: (eventId: EventId, senderId: UserId) -> Unit = { _, _ -> lambdaError() }, + private val onEditPollClickLambda: (eventId: EventId) -> Unit = { _ -> lambdaError() }, + private val onPreviewAttachmentLambda: (attachments: ImmutableList) -> Unit = { _ -> lambdaError() }, +) : MessagesNavigator { override fun onShowEventDebugInfoClick(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { - onShowEventDebugInfoClickedCount++ + onShowEventDebugInfoClickLambda(eventId, debugInfo) } override fun onForwardEventClick(eventId: EventId) { - onForwardEventClickedCount++ + onForwardEventClickLambda(eventId) } override fun onReportContentClick(eventId: EventId, senderId: UserId) { - onReportContentClickedCount++ + onReportContentClickLambda(eventId, senderId) } override fun onEditPollClick(eventId: EventId) { - onEditPollClickedCount++ + onEditPollClickLambda(eventId) + } + + override fun onPreviewAttachment(attachments: ImmutableList) { + onPreviewAttachmentLambda(attachments) } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt index 3d4969fd28..61606a51de 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt @@ -23,8 +23,8 @@ import io.element.android.features.messages.impl.messagecomposer.MessageComposer import io.element.android.features.messages.impl.messagecomposer.aMessageComposerState import io.element.android.features.messages.impl.pinned.banner.aLoadedPinnedMessagesBannerState import io.element.android.features.messages.impl.timeline.TimelineController -import io.element.android.features.messages.impl.timeline.TimelinePresenter -import io.element.android.features.messages.impl.timeline.createTimelinePresenter +import io.element.android.features.messages.impl.timeline.TimelineEvents +import io.element.android.features.messages.impl.timeline.aTimelineState import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent @@ -36,8 +36,6 @@ import io.element.android.features.messages.impl.timeline.protection.aTimelinePr import io.element.android.features.messages.impl.voicemessages.composer.aVoiceMessageComposerState import io.element.android.features.messages.test.timeline.FakeHtmlConverterProvider import io.element.android.features.networkmonitor.test.FakeNetworkMonitor -import io.element.android.features.poll.api.actions.EndPollAction -import io.element.android.features.poll.test.actions.FakeEndPollAction import io.element.android.features.roomcall.api.aStandByCallState import io.element.android.libraries.androidutils.clipboard.FakeClipboardHelper import io.element.android.libraries.architecture.AsyncData @@ -56,6 +54,7 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState import io.element.android.libraries.matrix.api.room.MessageEventType import io.element.android.libraries.matrix.api.room.RoomMembershipState +import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId import io.element.android.libraries.matrix.api.timeline.item.event.toEventOrTransactionId import io.element.android.libraries.matrix.test.AN_AVATAR_URL @@ -65,12 +64,14 @@ import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.A_SESSION_ID_2 import io.element.android.libraries.matrix.test.A_THROWABLE +import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.test.room.aRoomInfo import io.element.android.libraries.matrix.test.room.aRoomMember import io.element.android.libraries.matrix.test.timeline.FakeTimeline +import io.element.android.libraries.matrix.test.timeline.aTimelineItemDebugInfo import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails import io.element.android.libraries.textcomposer.model.MessageComposerMode import io.element.android.libraries.textcomposer.model.TextEditorState @@ -217,7 +218,10 @@ class MessagesPresenterTest { @Test fun `present - handle action forward`() = runTest { - val navigator = FakeMessagesNavigator() + val onForwardEventClickLambda = lambdaRecorder { } + val navigator = FakeMessagesNavigator( + onForwardEventClickLambda = onForwardEventClickLambda, + ) val presenter = createMessagesPresenter(navigator = navigator) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -225,7 +229,7 @@ class MessagesPresenterTest { val initialState = awaitItem() initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.Forward, aMessageEvent())) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) - assertThat(navigator.onForwardEventClickedCount).isEqualTo(1) + onForwardEventClickLambda.assertions().isCalledOnce().with(value(AN_EVENT_ID)) } } @@ -452,7 +456,10 @@ class MessagesPresenterTest { @Test fun `present - handle action edit poll`() = runTest { - val navigator = FakeMessagesNavigator() + val onEditPollClickLambda = lambdaRecorder { } + val navigator = FakeMessagesNavigator( + onEditPollClickLambda = onEditPollClickLambda + ) val presenter = createMessagesPresenter(navigator = navigator) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -460,22 +467,21 @@ class MessagesPresenterTest { val initialState = awaitItem() initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.Edit, aMessageEvent(content = aTimelineItemPollContent()))) awaitItem() - assertThat(navigator.onEditPollClickedCount).isEqualTo(1) + onEditPollClickLambda.assertions().isCalledOnce().with(value(AN_EVENT_ID)) } } @Test fun `present - handle action end poll`() = runTest { - val endPollAction = FakeEndPollAction() - val presenter = createMessagesPresenter(endPollAction = endPollAction) + val timelineEventSink = EventsRecorder() + val presenter = createMessagesPresenter(timelineEventSink = timelineEventSink) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() - endPollAction.verifyExecutionCount(0) initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.EndPoll, aMessageEvent(content = aTimelineItemPollContent()))) delay(1) - endPollAction.verifyExecutionCount(1) + timelineEventSink.assertSingle(TimelineEvents.EndPoll(AN_EVENT_ID)) cancelAndIgnoreRemainingEvents() } } @@ -516,7 +522,10 @@ class MessagesPresenterTest { @Test fun `present - handle action report content`() = runTest { - val navigator = FakeMessagesNavigator() + val onReportContentClickLambda = lambdaRecorder { _: EventId, _: UserId -> } + val navigator = FakeMessagesNavigator( + onReportContentClickLambda = onReportContentClickLambda + ) val presenter = createMessagesPresenter(navigator = navigator) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -524,7 +533,7 @@ class MessagesPresenterTest { val initialState = awaitItem() initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.ReportContent, aMessageEvent())) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) - assertThat(navigator.onReportContentClickedCount).isEqualTo(1) + onReportContentClickLambda.assertions().isCalledOnce().with(value(AN_EVENT_ID), value(A_USER_ID)) } } @@ -542,7 +551,10 @@ class MessagesPresenterTest { @Test fun `present - handle action show developer info`() = runTest { - val navigator = FakeMessagesNavigator() + val onShowEventDebugInfoClickLambda = lambdaRecorder { _: EventId?, _: TimelineItemDebugInfo -> } + val navigator = FakeMessagesNavigator( + onShowEventDebugInfoClickLambda = onShowEventDebugInfoClickLambda + ) val presenter = createMessagesPresenter(navigator = navigator) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -550,7 +562,7 @@ class MessagesPresenterTest { val initialState = awaitItem() initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.ViewSource, aMessageEvent())) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) - assertThat(navigator.onShowEventDebugInfoClickedCount).isEqualTo(1) + onShowEventDebugInfoClickLambda.assertions().isCalledOnce().with(value(AN_EVENT_ID), value(aTimelineItemDebugInfo())) } } @@ -1087,7 +1099,7 @@ class MessagesPresenterTest { navigator: FakeMessagesNavigator = FakeMessagesNavigator(), clipboardHelper: FakeClipboardHelper = FakeClipboardHelper(), analyticsService: FakeAnalyticsService = FakeAnalyticsService(), - endPollAction: EndPollAction = FakeEndPollAction(), + timelineEventSink: (TimelineEvents) -> Unit = {}, permalinkParser: PermalinkParser = FakePermalinkParser(), messageComposerPresenter: Presenter = Presenter { aMessageComposerState( @@ -1097,19 +1109,12 @@ class MessagesPresenterTest { }, actionListEventSink: (ActionListEvents) -> Unit = {}, ): MessagesPresenter { - val timelinePresenterFactory = object : TimelinePresenter.Factory { - override fun create(navigator: MessagesNavigator): TimelinePresenter { - return createTimelinePresenter( - endPollAction = endPollAction, - ) - } - } val featureFlagService = FakeFeatureFlagService() return MessagesPresenter( room = matrixRoom, composerPresenter = messageComposerPresenter, voiceMessageComposerPresenter = { aVoiceMessageComposerState() }, - timelinePresenterFactory = timelinePresenterFactory, + timelinePresenter = { aTimelineState(eventSink = timelineEventSink) }, timelineProtectionPresenter = { aTimelineProtectionState() }, actionListPresenterFactory = FakeActionListPresenter.Factory(actionListEventSink), customReactionPresenter = { aCustomReactionState() }, 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..1d4e1a43b3 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 @@ -33,7 +33,6 @@ import io.element.android.features.messages.impl.actionlist.ActionListEvents import io.element.android.features.messages.impl.actionlist.ActionListState import io.element.android.features.messages.impl.actionlist.anActionListState 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.sendfailure.VerifiedUserSendFailure import io.element.android.features.messages.impl.crypto.sendfailure.resolve.aChangedIdentitySendFailure import io.element.android.features.messages.impl.messagecomposer.aMessageComposerState @@ -64,7 +63,6 @@ import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.ensureCalledOnceWithParam import io.element.android.tests.testutils.pressBack -import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import org.junit.Rule import org.junit.Test @@ -514,7 +512,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 +529,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..d0b41bbb1e 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,9 @@ 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.attachments.Attachment 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 @@ -91,6 +94,7 @@ import io.element.android.tests.testutils.lambda.value import io.element.android.tests.testutils.test import io.element.android.tests.testutils.waitForPredicate import io.mockk.mockk +import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -133,7 +137,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 +688,15 @@ class MessageComposerPresenterTest { val room = FakeMatrixRoom( typingNoticeResult = { Result.success(Unit) } ) - val presenter = createPresenter(this, room = room) + val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList -> } + val navigator = FakeMessagesNavigator( + onPreviewAttachmentLambda = onPreviewAttachmentLambda + ) + val presenter = createPresenter( + coroutineScope = this, + room = room, + navigator = navigator, + ) pickerProvider.givenMimeType(MimeTypes.Images) mediaPreProcessor.givenResult( Result.success( @@ -709,9 +720,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) + onPreviewAttachmentLambda.assertions().isCalledOnce() } } @@ -720,7 +729,15 @@ class MessageComposerPresenterTest { val room = FakeMatrixRoom( typingNoticeResult = { Result.success(Unit) } ) - val presenter = createPresenter(this, room = room) + val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList -> } + val navigator = FakeMessagesNavigator( + onPreviewAttachmentLambda = onPreviewAttachmentLambda + ) + val presenter = createPresenter( + coroutineScope = this, + room = room, + navigator = navigator, + ) pickerProvider.givenMimeType(MimeTypes.Videos) mediaPreProcessor.givenResult( Result.success( @@ -745,9 +762,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) + onPreviewAttachmentLambda.assertions().isCalledOnce() } } @@ -772,15 +787,21 @@ class MessageComposerPresenterTest { val room = FakeMatrixRoom( typingNoticeResult = { Result.success(Unit) } ) - val presenter = createPresenter(this, room = room) + val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList -> } + val navigator = FakeMessagesNavigator( + onPreviewAttachmentLambda = onPreviewAttachmentLambda + ) + 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) + onPreviewAttachmentLambda.assertions().isCalledOnce() } } @@ -828,19 +849,22 @@ class MessageComposerPresenterTest { typingNoticeResult = { Result.success(Unit) } ) val permissionPresenter = FakePermissionsPresenter().apply { setPermissionGranted() } + val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList -> } + val navigator = FakeMessagesNavigator( + onPreviewAttachmentLambda = onPreviewAttachmentLambda + ) 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() + onPreviewAttachmentLambda.assertions().isCalledOnce() } } @@ -850,23 +874,23 @@ class MessageComposerPresenterTest { typingNoticeResult = { Result.success(Unit) } ) val permissionPresenter = FakePermissionsPresenter() + val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList -> } + val navigator = FakeMessagesNavigator( + onPreviewAttachmentLambda = onPreviewAttachmentLambda + ) 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) + onPreviewAttachmentLambda.assertions().isCalledOnce() cancelAndIgnoreRemainingEvents() } } @@ -877,19 +901,22 @@ class MessageComposerPresenterTest { typingNoticeResult = { Result.success(Unit) } ) val permissionPresenter = FakePermissionsPresenter().apply { setPermissionGranted() } + val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList -> } + val navigator = FakeMessagesNavigator( + onPreviewAttachmentLambda = onPreviewAttachmentLambda + ) 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() + onPreviewAttachmentLambda.assertions().isCalledOnce() } } @@ -899,10 +926,15 @@ class MessageComposerPresenterTest { typingNoticeResult = { Result.success(Unit) } ) val permissionPresenter = FakePermissionsPresenter() + val onPreviewAttachmentLambda = lambdaRecorder { _: ImmutableList -> } + val navigator = FakeMessagesNavigator( + onPreviewAttachmentLambda = onPreviewAttachmentLambda + ) val presenter = createPresenter( - this, + coroutineScope = this, room = room, permissionPresenter = permissionPresenter, + navigator = navigator, ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -911,12 +943,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() + onPreviewAttachmentLambda.assertions().isCalledOnce() } } @@ -1500,6 +1529,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 +1544,7 @@ class MessageComposerPresenterTest { isRichTextEditorEnabled: Boolean = true, draftService: ComposerDraftService = FakeComposerDraftService(), ) = MessageComposerPresenter( + navigator = navigator, appCoroutineScope = coroutineScope, room = room, mediaPickerProvider = pickerProvider, diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt index d153dd5743..a21dcb2834 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt @@ -431,7 +431,10 @@ import kotlin.time.Duration.Companion.seconds @Test fun `present - PollEditClicked event navigates`() = runTest { - val navigator = FakeMessagesNavigator() + val onEditPollClickLambda = lambdaRecorder { _: EventId -> } + val navigator = FakeMessagesNavigator( + onEditPollClickLambda = onEditPollClickLambda + ) val presenter = createTimelinePresenter( messagesNavigator = navigator, ) @@ -439,7 +442,7 @@ import kotlin.time.Duration.Companion.seconds presenter.present() }.test { awaitFirstItem().eventSink(TimelineEvents.EditPoll(AN_EVENT_ID)) - assertThat(navigator.onEditPollClickedCount).isEqualTo(1) + onEditPollClickLambda.assertions().isCalledOnce().with(value(AN_EVENT_ID)) } } @@ -657,35 +660,35 @@ import kotlin.time.Duration.Companion.seconds private suspend fun ReceiveTurbine.awaitFirstItem(): T { return awaitItem() } -} -internal fun TestScope.createTimelinePresenter( - timeline: Timeline = FakeTimeline(), - room: FakeMatrixRoom = FakeMatrixRoom( - liveTimeline = timeline, - canUserSendMessageResult = { _, _ -> Result.success(true) } - ), - redactedVoiceMessageManager: RedactedVoiceMessageManager = FakeRedactedVoiceMessageManager(), - messagesNavigator: FakeMessagesNavigator = FakeMessagesNavigator(), - endPollAction: EndPollAction = FakeEndPollAction(), - sendPollResponseAction: SendPollResponseAction = FakeSendPollResponseAction(), - sessionPreferencesStore: InMemorySessionPreferencesStore = InMemorySessionPreferencesStore(), - timelineItemIndexer: TimelineItemIndexer = TimelineItemIndexer(), -): TimelinePresenter { - return TimelinePresenter( - timelineItemsFactoryCreator = aTimelineItemsFactoryCreator(), - room = room, - dispatchers = testCoroutineDispatchers(), - appScope = this, - navigator = messagesNavigator, - redactedVoiceMessageManager = redactedVoiceMessageManager, - endPollAction = endPollAction, - sendPollResponseAction = sendPollResponseAction, - sessionPreferencesStore = sessionPreferencesStore, - timelineItemIndexer = timelineItemIndexer, - timelineController = TimelineController(room), - resolveVerifiedUserSendFailurePresenter = { aResolveVerifiedUserSendFailureState() }, - typingNotificationPresenter = { aTypingNotificationState() }, - roomCallStatePresenter = { aStandByCallState() }, - ) + private fun TestScope.createTimelinePresenter( + timeline: Timeline = FakeTimeline(), + room: FakeMatrixRoom = FakeMatrixRoom( + liveTimeline = timeline, + canUserSendMessageResult = { _, _ -> Result.success(true) } + ), + redactedVoiceMessageManager: RedactedVoiceMessageManager = FakeRedactedVoiceMessageManager(), + messagesNavigator: FakeMessagesNavigator = FakeMessagesNavigator(), + endPollAction: EndPollAction = FakeEndPollAction(), + sendPollResponseAction: SendPollResponseAction = FakeSendPollResponseAction(), + sessionPreferencesStore: InMemorySessionPreferencesStore = InMemorySessionPreferencesStore(), + timelineItemIndexer: TimelineItemIndexer = TimelineItemIndexer(), + ): TimelinePresenter { + return TimelinePresenter( + timelineItemsFactoryCreator = aTimelineItemsFactoryCreator(), + room = room, + dispatchers = testCoroutineDispatchers(), + appScope = this, + navigator = messagesNavigator, + redactedVoiceMessageManager = redactedVoiceMessageManager, + endPollAction = endPollAction, + sendPollResponseAction = sendPollResponseAction, + sessionPreferencesStore = sessionPreferencesStore, + timelineItemIndexer = timelineItemIndexer, + timelineController = TimelineController(room), + resolveVerifiedUserSendFailurePresenter = { aResolveVerifiedUserSendFailureState() }, + typingNotificationPresenter = { aTypingNotificationState() }, + roomCallStatePresenter = { aStandByCallState() }, + ) + } }