Remove AttachmentsState and use the MessagesNavigator

This commit is contained in:
Benoit Marty
2024-11-21 17:07:34 +01:00
parent b37cabd703
commit 5ec0d5a5f9
12 changed files with 86 additions and 116 deletions

View File

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

View File

@@ -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<Plugin>,
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<Callback>()
data class Inputs(val focusedEventId: EventId?) : NodeInputs
@@ -114,10 +119,6 @@ class MessagesNode @AssistedInject constructor(
.orFalse()
}
private fun onPreviewAttachments(attachments: ImmutableList<Attachment>) {
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<Attachment>) {
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,

View File

@@ -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<MessageComposerState>,
@Assisted private val composerPresenter: Presenter<MessageComposerState>,
private val voiceMessageComposerPresenter: Presenter<VoiceMessageComposerState>,
timelinePresenterFactory: TimelinePresenter.Factory,
private val timelineProtectionPresenter: Presenter<TimelineProtectionState>,
private val identityChangeStatePresenter: Presenter<IdentityChangeState>,
private val actionListPresenterFactory: ActionListPresenter.Factory,
actionListPresenterFactory: ActionListPresenter.Factory,
private val customReactionPresenter: Presenter<CustomReactionState>,
private val reactionSummaryPresenter: Presenter<ReactionSummaryState>,
private val readReceiptBottomSheetPresenter: Presenter<ReadReceiptBottomSheetState>,
@@ -116,7 +116,10 @@ class MessagesPresenter @AssistedInject constructor(
@AssistedFactory
interface Factory {
fun create(navigator: MessagesNavigator): MessagesPresenter
fun create(
navigator: MessagesNavigator,
composerPresenter: Presenter<MessageComposerState>,
): MessagesPresenter
}
@Composable

View File

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

View File

@@ -36,7 +36,6 @@ internal fun MessagesViewWithIdentityChangePreview(
onEventContentClick = { false },
onUserDataClick = {},
onLinkClick = {},
onPreviewAttachments = {},
onSendLocationClick = {},
onCreatePollClick = {},
onJoinCallClick = {},

View File

@@ -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<TimelineProtectionState>
@Binds
fun bindMessageComposerPresenter(presenter: MessageComposerPresenter): Presenter<MessageComposerState>
@Binds
fun bindVoiceMessageComposerPresenter(presenter: VoiceMessageComposerPresenter): Presenter<VoiceMessageComposerState>

View File

@@ -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<MessageComposerState> {
@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<Suggestion?>(null)
@@ -147,9 +152,6 @@ class MessageComposerPresenter @Inject constructor(
}
val cameraPermissionState = cameraPermissionPresenter.present()
val attachmentsState = remember {
mutableStateOf<AttachmentsState>(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<AttachmentsState>,
) = 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<AttachmentsState>,
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<AttachmentsState>,
) = 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 {

View File

@@ -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<ResolvedSuggestion>,
val resolveMentionDisplay: (String, String) -> TextDisplay,
val eventSink: (MessageComposerEvents) -> Unit,
)
@Immutable
sealed interface AttachmentsState {
data object None : AttachmentsState
data class Previewing(val attachments: ImmutableList<Attachment>) : AttachmentsState
}

View File

@@ -31,7 +31,6 @@ fun aMessageComposerState(
showAttachmentSourcePicker: Boolean = false,
canShareLocation: Boolean = true,
canCreatePoll: Boolean = true,
attachmentsState: AttachmentsState = AttachmentsState.None,
suggestions: ImmutableList<ResolvedSuggestion> = 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,

View File

@@ -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<Attachment>) {
onPreviewAttachmentCount++
}
}

View File

@@ -514,7 +514,6 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setMessa
onEventClick: (event: TimelineItem.Event) -> Boolean = EnsureNeverCalledWithParamAndResult(),
onUserDataClick: (UserId) -> Unit = EnsureNeverCalledWithParam(),
onLinkClick: (String) -> Unit = EnsureNeverCalledWithParam(),
onPreviewAttachments: (ImmutableList<Attachment>) -> Unit = EnsureNeverCalledWithParam(),
onSendLocationClick: () -> Unit = EnsureNeverCalled(),
onCreatePollClick: () -> Unit = EnsureNeverCalled(),
onJoinCallClick: () -> Unit = EnsureNeverCalled(),
@@ -532,7 +531,6 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setMessa
onEventContentClick = onEventClick,
onUserDataClick = onUserDataClick,
onLinkClick = onLinkClick,
onPreviewAttachments = onPreviewAttachments,
onSendLocationClick = onSendLocationClick,
onCreatePollClick = onCreatePollClick,
onJoinCallClick = onJoinCallClick,

View File

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