Let the TimelinePresenter.Factory be injected in MessagesNode to have a better architecture of dependencies between presenter.

This commit is contained in:
Benoit Marty 2024-11-21 17:29:20 +01:00
parent 682abb3eff
commit a81170ef74
4 changed files with 46 additions and 54 deletions

View file

@ -32,6 +32,7 @@ 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.MessageComposerEvents
import io.element.android.features.messages.impl.messagecomposer.MessageComposerPresenter 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.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.LocalTimelineItemPresenterFactories
import io.element.android.features.messages.impl.timeline.di.TimelineItemPresenterFactories import io.element.android.features.messages.impl.timeline.di.TimelineItemPresenterFactories
import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.TimelineItem
@ -62,6 +63,7 @@ class MessagesNode @AssistedInject constructor(
private val room: MatrixRoom, private val room: MatrixRoom,
private val analyticsService: AnalyticsService, private val analyticsService: AnalyticsService,
messageComposerPresenterFactory: MessageComposerPresenter.Factory, messageComposerPresenterFactory: MessageComposerPresenter.Factory,
timelinePresenterFactory: TimelinePresenter.Factory,
presenterFactory: MessagesPresenter.Factory, presenterFactory: MessagesPresenter.Factory,
private val timelineItemPresenterFactories: TimelineItemPresenterFactories, private val timelineItemPresenterFactories: TimelineItemPresenterFactories,
private val mediaPlayer: MediaPlayer, private val mediaPlayer: MediaPlayer,
@ -70,6 +72,7 @@ class MessagesNode @AssistedInject constructor(
private val presenter = presenterFactory.create( private val presenter = presenterFactory.create(
navigator = this, navigator = this,
composerPresenter = messageComposerPresenterFactory.create(this), composerPresenter = messageComposerPresenterFactory.create(this),
timelinePresenter = timelinePresenterFactory.create(this),
) )
private val callbacks = plugins<Callback>() private val callbacks = plugins<Callback>()

View file

@ -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.pinned.banner.PinnedMessagesBannerState
import io.element.android.features.messages.impl.timeline.TimelineController 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.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.TimelineState
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionState import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionState
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryState import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryState
@ -91,7 +90,7 @@ class MessagesPresenter @AssistedInject constructor(
private val room: MatrixRoom, private val room: MatrixRoom,
@Assisted private val composerPresenter: Presenter<MessageComposerState>, @Assisted private val composerPresenter: Presenter<MessageComposerState>,
private val voiceMessageComposerPresenter: Presenter<VoiceMessageComposerState>, private val voiceMessageComposerPresenter: Presenter<VoiceMessageComposerState>,
timelinePresenterFactory: TimelinePresenter.Factory, @Assisted private val timelinePresenter: Presenter<TimelineState>,
private val timelineProtectionPresenter: Presenter<TimelineProtectionState>, private val timelineProtectionPresenter: Presenter<TimelineProtectionState>,
private val identityChangeStatePresenter: Presenter<IdentityChangeState>, private val identityChangeStatePresenter: Presenter<IdentityChangeState>,
actionListPresenterFactory: ActionListPresenter.Factory, actionListPresenterFactory: ActionListPresenter.Factory,
@ -111,7 +110,6 @@ class MessagesPresenter @AssistedInject constructor(
private val permalinkParser: PermalinkParser, private val permalinkParser: PermalinkParser,
private val analyticsService: AnalyticsService, private val analyticsService: AnalyticsService,
) : Presenter<MessagesState> { ) : Presenter<MessagesState> {
private val timelinePresenter = timelinePresenterFactory.create(navigator = navigator)
private val actionListPresenter = actionListPresenterFactory.create(TimelineItemActionPostProcessor.Default) private val actionListPresenter = actionListPresenterFactory.create(TimelineItemActionPostProcessor.Default)
@AssistedFactory @AssistedFactory
@ -119,6 +117,7 @@ class MessagesPresenter @AssistedInject constructor(
fun create( fun create(
navigator: MessagesNavigator, navigator: MessagesNavigator,
composerPresenter: Presenter<MessageComposerState>, composerPresenter: Presenter<MessageComposerState>,
timelinePresenter: Presenter<TimelineState>,
): MessagesPresenter ): MessagesPresenter
} }

View file

@ -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.messagecomposer.aMessageComposerState
import io.element.android.features.messages.impl.pinned.banner.aLoadedPinnedMessagesBannerState 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.TimelineController
import io.element.android.features.messages.impl.timeline.TimelinePresenter import io.element.android.features.messages.impl.timeline.TimelineEvents
import io.element.android.features.messages.impl.timeline.createTimelinePresenter 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.TimelineItemFileContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent 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.impl.voicemessages.composer.aVoiceMessageComposerState
import io.element.android.features.messages.test.timeline.FakeHtmlConverterProvider import io.element.android.features.messages.test.timeline.FakeHtmlConverterProvider
import io.element.android.features.networkmonitor.test.FakeNetworkMonitor 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.features.roomcall.api.aStandByCallState
import io.element.android.libraries.androidutils.clipboard.FakeClipboardHelper import io.element.android.libraries.androidutils.clipboard.FakeClipboardHelper
import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.AsyncData
@ -220,7 +218,7 @@ class MessagesPresenterTest {
@Test @Test
fun `present - handle action forward`() = runTest { fun `present - handle action forward`() = runTest {
val onForwardEventClickLambda = lambdaRecorder<EventId, Unit> { } val onForwardEventClickLambda = lambdaRecorder<EventId, Unit> { }
val navigator = FakeMessagesNavigator( val navigator = FakeMessagesNavigator(
onForwardEventClickLambda = onForwardEventClickLambda, onForwardEventClickLambda = onForwardEventClickLambda,
) )
@ -458,7 +456,7 @@ class MessagesPresenterTest {
@Test @Test
fun `present - handle action edit poll`() = runTest { fun `present - handle action edit poll`() = runTest {
val onEditPollClickLambda = lambdaRecorder<EventId, Unit> { } val onEditPollClickLambda = lambdaRecorder<EventId, Unit> { }
val navigator = FakeMessagesNavigator( val navigator = FakeMessagesNavigator(
onEditPollClickLambda = onEditPollClickLambda onEditPollClickLambda = onEditPollClickLambda
) )
@ -475,16 +473,15 @@ class MessagesPresenterTest {
@Test @Test
fun `present - handle action end poll`() = runTest { fun `present - handle action end poll`() = runTest {
val endPollAction = FakeEndPollAction() val timelineEventSink = EventsRecorder<TimelineEvents>()
val presenter = createMessagesPresenter(endPollAction = endPollAction) val presenter = createMessagesPresenter(timelineEventSink = timelineEventSink)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
val initialState = awaitItem() val initialState = awaitItem()
endPollAction.verifyExecutionCount(0)
initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.EndPoll, aMessageEvent(content = aTimelineItemPollContent()))) initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.EndPoll, aMessageEvent(content = aTimelineItemPollContent())))
delay(1) delay(1)
endPollAction.verifyExecutionCount(1) timelineEventSink.assertSingle(TimelineEvents.EndPoll(AN_EVENT_ID))
cancelAndIgnoreRemainingEvents() cancelAndIgnoreRemainingEvents()
} }
} }
@ -525,7 +522,7 @@ class MessagesPresenterTest {
@Test @Test
fun `present - handle action report content`() = runTest { fun `present - handle action report content`() = runTest {
val onReportContentClickLambda = lambdaRecorder { _: EventId, _: UserId -> } val onReportContentClickLambda = lambdaRecorder { _: EventId, _: UserId -> }
val navigator = FakeMessagesNavigator( val navigator = FakeMessagesNavigator(
onReportContentClickLambda = onReportContentClickLambda onReportContentClickLambda = onReportContentClickLambda
) )
@ -554,7 +551,7 @@ class MessagesPresenterTest {
@Test @Test
fun `present - handle action show developer info`() = runTest { fun `present - handle action show developer info`() = runTest {
val onShowEventDebugInfoClickLambda = lambdaRecorder { _: EventId?, _: TimelineItemDebugInfo -> } val onShowEventDebugInfoClickLambda = lambdaRecorder { _: EventId?, _: TimelineItemDebugInfo -> }
val navigator = FakeMessagesNavigator( val navigator = FakeMessagesNavigator(
onShowEventDebugInfoClickLambda = onShowEventDebugInfoClickLambda onShowEventDebugInfoClickLambda = onShowEventDebugInfoClickLambda
) )
@ -1102,7 +1099,7 @@ class MessagesPresenterTest {
navigator: FakeMessagesNavigator = FakeMessagesNavigator(), navigator: FakeMessagesNavigator = FakeMessagesNavigator(),
clipboardHelper: FakeClipboardHelper = FakeClipboardHelper(), clipboardHelper: FakeClipboardHelper = FakeClipboardHelper(),
analyticsService: FakeAnalyticsService = FakeAnalyticsService(), analyticsService: FakeAnalyticsService = FakeAnalyticsService(),
endPollAction: EndPollAction = FakeEndPollAction(), timelineEventSink: (TimelineEvents) -> Unit = {},
permalinkParser: PermalinkParser = FakePermalinkParser(), permalinkParser: PermalinkParser = FakePermalinkParser(),
messageComposerPresenter: Presenter<MessageComposerState> = Presenter { messageComposerPresenter: Presenter<MessageComposerState> = Presenter {
aMessageComposerState( aMessageComposerState(
@ -1112,19 +1109,12 @@ class MessagesPresenterTest {
}, },
actionListEventSink: (ActionListEvents) -> Unit = {}, actionListEventSink: (ActionListEvents) -> Unit = {},
): MessagesPresenter { ): MessagesPresenter {
val timelinePresenterFactory = object : TimelinePresenter.Factory {
override fun create(navigator: MessagesNavigator): TimelinePresenter {
return createTimelinePresenter(
endPollAction = endPollAction,
)
}
}
val featureFlagService = FakeFeatureFlagService() val featureFlagService = FakeFeatureFlagService()
return MessagesPresenter( return MessagesPresenter(
room = matrixRoom, room = matrixRoom,
composerPresenter = messageComposerPresenter, composerPresenter = messageComposerPresenter,
voiceMessageComposerPresenter = { aVoiceMessageComposerState() }, voiceMessageComposerPresenter = { aVoiceMessageComposerState() },
timelinePresenterFactory = timelinePresenterFactory, timelinePresenter = { aTimelineState(eventSink = timelineEventSink) },
timelineProtectionPresenter = { aTimelineProtectionState() }, timelineProtectionPresenter = { aTimelineProtectionState() },
actionListPresenterFactory = FakeActionListPresenter.Factory(actionListEventSink), actionListPresenterFactory = FakeActionListPresenter.Factory(actionListEventSink),
customReactionPresenter = { aCustomReactionState() }, customReactionPresenter = { aCustomReactionState() },

View file

@ -660,35 +660,35 @@ import kotlin.time.Duration.Companion.seconds
private suspend fun <T> ReceiveTurbine<T>.awaitFirstItem(): T { private suspend fun <T> ReceiveTurbine<T>.awaitFirstItem(): T {
return awaitItem() return awaitItem()
} }
}
internal fun TestScope.createTimelinePresenter( private fun TestScope.createTimelinePresenter(
timeline: Timeline = FakeTimeline(), timeline: Timeline = FakeTimeline(),
room: FakeMatrixRoom = FakeMatrixRoom( room: FakeMatrixRoom = FakeMatrixRoom(
liveTimeline = timeline, liveTimeline = timeline,
canUserSendMessageResult = { _, _ -> Result.success(true) } canUserSendMessageResult = { _, _ -> Result.success(true) }
), ),
redactedVoiceMessageManager: RedactedVoiceMessageManager = FakeRedactedVoiceMessageManager(), redactedVoiceMessageManager: RedactedVoiceMessageManager = FakeRedactedVoiceMessageManager(),
messagesNavigator: FakeMessagesNavigator = FakeMessagesNavigator(), messagesNavigator: FakeMessagesNavigator = FakeMessagesNavigator(),
endPollAction: EndPollAction = FakeEndPollAction(), endPollAction: EndPollAction = FakeEndPollAction(),
sendPollResponseAction: SendPollResponseAction = FakeSendPollResponseAction(), sendPollResponseAction: SendPollResponseAction = FakeSendPollResponseAction(),
sessionPreferencesStore: InMemorySessionPreferencesStore = InMemorySessionPreferencesStore(), sessionPreferencesStore: InMemorySessionPreferencesStore = InMemorySessionPreferencesStore(),
timelineItemIndexer: TimelineItemIndexer = TimelineItemIndexer(), timelineItemIndexer: TimelineItemIndexer = TimelineItemIndexer(),
): TimelinePresenter { ): TimelinePresenter {
return TimelinePresenter( return TimelinePresenter(
timelineItemsFactoryCreator = aTimelineItemsFactoryCreator(), timelineItemsFactoryCreator = aTimelineItemsFactoryCreator(),
room = room, room = room,
dispatchers = testCoroutineDispatchers(), dispatchers = testCoroutineDispatchers(),
appScope = this, appScope = this,
navigator = messagesNavigator, navigator = messagesNavigator,
redactedVoiceMessageManager = redactedVoiceMessageManager, redactedVoiceMessageManager = redactedVoiceMessageManager,
endPollAction = endPollAction, endPollAction = endPollAction,
sendPollResponseAction = sendPollResponseAction, sendPollResponseAction = sendPollResponseAction,
sessionPreferencesStore = sessionPreferencesStore, sessionPreferencesStore = sessionPreferencesStore,
timelineItemIndexer = timelineItemIndexer, timelineItemIndexer = timelineItemIndexer,
timelineController = TimelineController(room), timelineController = TimelineController(room),
resolveVerifiedUserSendFailurePresenter = { aResolveVerifiedUserSendFailureState() }, resolveVerifiedUserSendFailurePresenter = { aResolveVerifiedUserSendFailureState() },
typingNotificationPresenter = { aTypingNotificationState() }, typingNotificationPresenter = { aTypingNotificationState() },
roomCallStatePresenter = { aStandByCallState() }, roomCallStatePresenter = { aStandByCallState() },
) )
}
} }