From aaefa8e740bb884a7a6b97b3673caff2aee9bd6e Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 9 Sep 2024 14:49:53 +0200 Subject: [PATCH] Pinned messages list : hide reactions. --- .../list/PinnedMessagesListPresenter.kt | 9 ++- .../impl/timeline/TimelinePresenter.kt | 10 ++- .../factories/TimelineItemsFactory.kt | 15 +++- .../factories/TimelineItemsFactoryConfig.kt | 18 +++++ .../event/TimelineItemEventFactory.kt | 22 +++++- .../messages/impl/MessagesPresenterTest.kt | 4 +- .../fixtures/TimelineItemsFactoryFixtures.kt | 72 ++++++++++++------- .../list/PinnedMessagesListPresenterTest.kt | 4 +- .../impl/timeline/TimelinePresenterTest.kt | 6 +- 9 files changed, 117 insertions(+), 43 deletions(-) create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactoryConfig.kt diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt index f005690ef0..6c06bb5474 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt @@ -27,6 +27,7 @@ import io.element.android.features.messages.impl.actionlist.model.TimelineItemAc import io.element.android.features.messages.impl.pinned.PinnedEventsTimelineProvider import io.element.android.features.messages.impl.timeline.TimelineRoomInfo import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory +import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactoryConfig import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter @@ -55,7 +56,7 @@ import kotlin.time.Duration.Companion.milliseconds class PinnedMessagesListPresenter @AssistedInject constructor( @Assisted private val navigator: PinnedMessagesListNavigator, private val room: MatrixRoom, - private val timelineItemsFactory: TimelineItemsFactory, + timelineItemsFactoryCreator: TimelineItemsFactory.Creator, private val timelineProvider: PinnedEventsTimelineProvider, private val snackbarDispatcher: SnackbarDispatcher, actionListPresenterFactory: ActionListPresenter.Factory, @@ -65,6 +66,12 @@ class PinnedMessagesListPresenter @AssistedInject constructor( fun create(navigator: PinnedMessagesListNavigator): PinnedMessagesListPresenter } + private val timelineItemsFactory: TimelineItemsFactory = timelineItemsFactoryCreator.create( + config = TimelineItemsFactoryConfig( + computeReadReceipts = false, + computeReactions = false, + ) + ) private val actionListPresenter = actionListPresenterFactory.create(PinnedMessagesListTimelineActionPostProcessor()) @Composable diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt index 9466ac16f5..e4287cb3ed 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt @@ -22,6 +22,7 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import io.element.android.features.messages.impl.MessagesNavigator import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory +import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactoryConfig import io.element.android.features.messages.impl.timeline.model.NewEventState import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.voicemessages.timeline.RedactedVoiceMessageManager @@ -54,7 +55,7 @@ import kotlinx.coroutines.withContext const val FOCUS_ON_PINNED_EVENT_DEBOUNCE_DURATION_IN_MILLIS = 200L class TimelinePresenter @AssistedInject constructor( - private val timelineItemsFactory: TimelineItemsFactory, + timelineItemsFactoryCreator: TimelineItemsFactory.Creator, private val timelineItemIndexer: TimelineItemIndexer, private val room: MatrixRoom, private val dispatchers: CoroutineDispatchers, @@ -71,6 +72,13 @@ class TimelinePresenter @AssistedInject constructor( fun create(navigator: MessagesNavigator): TimelinePresenter } + private val timelineItemsFactory: TimelineItemsFactory = timelineItemsFactoryCreator.create( + config = TimelineItemsFactoryConfig( + computeReadReceipts = true, + computeReactions = true, + ) + ) + @Composable override fun present(): TimelineState { val localScope = rememberCoroutineScope() diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt index 18f48c5e11..f9857328cd 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt @@ -7,6 +7,9 @@ package io.element.android.features.messages.impl.timeline.factories +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import io.element.android.features.messages.impl.timeline.TimelineItemIndexer import io.element.android.features.messages.impl.timeline.diff.TimelineItemsCacheInvalidator import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemEventFactory @@ -26,15 +29,21 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext -import javax.inject.Inject -class TimelineItemsFactory @Inject constructor( +class TimelineItemsFactory @AssistedInject constructor( + @Assisted config: TimelineItemsFactoryConfig, + eventItemFactoryCreator: TimelineItemEventFactory.Creator, private val dispatchers: CoroutineDispatchers, - private val eventItemFactory: TimelineItemEventFactory, private val virtualItemFactory: TimelineItemVirtualFactory, private val timelineItemGrouper: TimelineItemGrouper, private val timelineItemIndexer: TimelineItemIndexer, ) { + @AssistedFactory + interface Creator { + fun create(config: TimelineItemsFactoryConfig): TimelineItemsFactory + } + + private val eventItemFactory = eventItemFactoryCreator.create(config) private val _timelineItems = MutableSharedFlow>(replay = 1) private val lock = Mutex() private val diffCache = MutableListDiffCache() diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactoryConfig.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactoryConfig.kt new file mode 100644 index 0000000000..3fbbc10acb --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactoryConfig.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.messages.impl.timeline.factories + +/** + * Some data used to configure the creation of timeline items. + * @param computeReadReceipts when false, read receipts will be empty. + * @param computeReactions when false, reactions will be empty. + */ +data class TimelineItemsFactoryConfig( + val computeReadReceipts: Boolean, + val computeReactions: Boolean, +) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt index dd7b5591fe..c71e9f6a26 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt @@ -7,6 +7,10 @@ package io.element.android.features.messages.impl.timeline.factories.event +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactoryConfig import io.element.android.features.messages.impl.timeline.groups.canBeDisplayedInBubbleBlock import io.element.android.features.messages.impl.timeline.model.AggregatedReaction import io.element.android.features.messages.impl.timeline.model.AggregatedReactionSender @@ -26,17 +30,23 @@ import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.item.event.getAvatarUrl import io.element.android.libraries.matrix.api.timeline.item.event.getDisambiguatedDisplayName import io.element.android.libraries.matrix.ui.messages.reply.map +import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import java.text.DateFormat import java.util.Date -import javax.inject.Inject -class TimelineItemEventFactory @Inject constructor( +class TimelineItemEventFactory @AssistedInject constructor( + @Assisted private val config: TimelineItemsFactoryConfig, private val contentFactory: TimelineItemContentFactory, private val matrixClient: MatrixClient, private val lastMessageTimestampFormatter: LastMessageTimestampFormatter, private val permalinkParser: PermalinkParser, ) { + @AssistedFactory + interface Creator { + fun create(config: TimelineItemsFactoryConfig): TimelineItemEventFactory + } + suspend fun create( currentTimelineItem: MatrixTimelineItem.Event, index: Int, @@ -92,8 +102,11 @@ class TimelineItemEventFactory @Inject constructor( } private fun MatrixTimelineItem.Event.computeReactionsState(): TimelineItemReactions { + if (!config.computeReactions) { + return TimelineItemReactions(reactions = persistentListOf()) + } val timeFormatter = DateFormat.getTimeInstance(DateFormat.SHORT) - var aggregatedReactions = event.reactions.map { reaction -> + var aggregatedReactions = this.event.reactions.map { reaction -> // Sort reactions within an aggregation by timestamp descending. // This puts the most recent at the top, useful in cases like the // reaction summary view or getting the most recent reaction. @@ -129,6 +142,9 @@ class TimelineItemEventFactory @Inject constructor( private fun MatrixTimelineItem.Event.computeReadReceiptState( roomMembers: List, ): TimelineItemReadReceipts { + if (!config.computeReadReceipts) { + return TimelineItemReadReceipts(receipts = persistentListOf()) + } return TimelineItemReadReceipts( receipts = event.receipts .map { receipt -> 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 6109b6f1c3..df873d2328 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 @@ -18,7 +18,7 @@ import io.element.android.features.messages.impl.actionlist.FakeActionListPresen import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction import io.element.android.features.messages.impl.draft.FakeComposerDraftService import io.element.android.features.messages.impl.fixtures.aMessageEvent -import io.element.android.features.messages.impl.fixtures.aTimelineItemsFactory +import io.element.android.features.messages.impl.fixtures.aTimelineItemsFactoryCreator import io.element.android.features.messages.impl.messagecomposer.DefaultMessageComposerContext import io.element.android.features.messages.impl.messagecomposer.FakeRoomAliasSuggestionsDataSource import io.element.android.features.messages.impl.messagecomposer.MessageComposerPresenter @@ -1024,7 +1024,7 @@ class MessagesPresenterTest { permissionsPresenterFactory, ) val timelinePresenter = TimelinePresenter( - timelineItemsFactory = aTimelineItemsFactory(), + timelineItemsFactoryCreator = aTimelineItemsFactoryCreator(), room = matrixRoom, dispatchers = coroutineDispatchers, appScope = this, diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/TimelineItemsFactoryFixtures.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/TimelineItemsFactoryFixtures.kt index ee6db627e5..c9439e0a3f 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/TimelineItemsFactoryFixtures.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/fixtures/TimelineItemsFactoryFixtures.kt @@ -9,6 +9,7 @@ package io.element.android.features.messages.impl.fixtures import io.element.android.features.messages.impl.timeline.TimelineItemIndexer import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory +import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactoryConfig import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemContentFactory import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemContentFailedToParseMessageFactory import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemContentFailedToParseStateFactory @@ -39,40 +40,56 @@ import io.element.android.libraries.mediaviewer.api.util.FileExtensionExtractorW import io.element.android.tests.testutils.testCoroutineDispatchers import kotlinx.coroutines.test.TestScope +internal fun TestScope.aTimelineItemsFactoryCreator( + timelineItemIndexer: TimelineItemIndexer = TimelineItemIndexer(), +): TimelineItemsFactory.Creator { + return object : TimelineItemsFactory.Creator { + override fun create(config: TimelineItemsFactoryConfig): TimelineItemsFactory { + return aTimelineItemsFactory(config, timelineItemIndexer) + } + } +} + internal fun TestScope.aTimelineItemsFactory( - timelineItemIndexer: TimelineItemIndexer = TimelineItemIndexer() + config: TimelineItemsFactoryConfig, + timelineItemIndexer: TimelineItemIndexer = TimelineItemIndexer(), ): TimelineItemsFactory { val timelineEventFormatter = aTimelineEventFormatter() val matrixClient = FakeMatrixClient() return TimelineItemsFactory( dispatchers = testCoroutineDispatchers(), - eventItemFactory = TimelineItemEventFactory( - contentFactory = TimelineItemContentFactory( - messageFactory = TimelineItemContentMessageFactory( - fileSizeFormatter = FakeFileSizeFormatter(), - fileExtensionExtractor = FileExtensionExtractorWithoutValidation(), - featureFlagService = FakeFeatureFlagService(), - htmlConverterProvider = FakeHtmlConverterProvider(), + eventItemFactoryCreator = object : TimelineItemEventFactory.Creator { + override fun create(config: TimelineItemsFactoryConfig): TimelineItemEventFactory { + return TimelineItemEventFactory( + contentFactory = TimelineItemContentFactory( + messageFactory = TimelineItemContentMessageFactory( + fileSizeFormatter = FakeFileSizeFormatter(), + fileExtensionExtractor = FileExtensionExtractorWithoutValidation(), + featureFlagService = FakeFeatureFlagService(), + htmlConverterProvider = FakeHtmlConverterProvider(), + permalinkParser = FakePermalinkParser(), + textPillificationHelper = FakeTextPillificationHelper(), + ), + redactedMessageFactory = TimelineItemContentRedactedFactory(), + stickerFactory = TimelineItemContentStickerFactory( + fileSizeFormatter = FakeFileSizeFormatter(), + fileExtensionExtractor = FileExtensionExtractorWithoutValidation() + ), + pollFactory = TimelineItemContentPollFactory(FakeFeatureFlagService(), FakePollContentStateFactory()), + utdFactory = TimelineItemContentUTDFactory(), + roomMembershipFactory = TimelineItemContentRoomMembershipFactory(timelineEventFormatter), + profileChangeFactory = TimelineItemContentProfileChangeFactory(timelineEventFormatter), + stateFactory = TimelineItemContentStateFactory(timelineEventFormatter), + failedToParseMessageFactory = TimelineItemContentFailedToParseMessageFactory(), + failedToParseStateFactory = TimelineItemContentFailedToParseStateFactory(), + ), + matrixClient = matrixClient, + lastMessageTimestampFormatter = FakeLastMessageTimestampFormatter(), permalinkParser = FakePermalinkParser(), - textPillificationHelper = FakeTextPillificationHelper(), - ), - redactedMessageFactory = TimelineItemContentRedactedFactory(), - stickerFactory = TimelineItemContentStickerFactory( - fileSizeFormatter = FakeFileSizeFormatter(), - fileExtensionExtractor = FileExtensionExtractorWithoutValidation() - ), - pollFactory = TimelineItemContentPollFactory(FakeFeatureFlagService(), FakePollContentStateFactory()), - utdFactory = TimelineItemContentUTDFactory(), - roomMembershipFactory = TimelineItemContentRoomMembershipFactory(timelineEventFormatter), - profileChangeFactory = TimelineItemContentProfileChangeFactory(timelineEventFormatter), - stateFactory = TimelineItemContentStateFactory(timelineEventFormatter), - failedToParseMessageFactory = TimelineItemContentFailedToParseMessageFactory(), - failedToParseStateFactory = TimelineItemContentFailedToParseStateFactory(), - ), - matrixClient = matrixClient, - lastMessageTimestampFormatter = FakeLastMessageTimestampFormatter(), - permalinkParser = FakePermalinkParser(), - ), + config = config + ) + } + }, virtualItemFactory = TimelineItemVirtualFactory( daySeparatorFactory = TimelineItemDaySeparatorFactory( FakeDaySeparatorFormatter() @@ -80,6 +97,7 @@ internal fun TestScope.aTimelineItemsFactory( ), timelineItemGrouper = TimelineItemGrouper(), timelineItemIndexer = timelineItemIndexer, + config = config ) } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenterTest.kt index 03927ec994..a3b5539f4f 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenterTest.kt @@ -10,7 +10,7 @@ package io.element.android.features.messages.impl.pinned.list import com.google.common.truth.Truth.assertThat import io.element.android.features.messages.impl.actionlist.FakeActionListPresenter import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction -import io.element.android.features.messages.impl.fixtures.aTimelineItemsFactory +import io.element.android.features.messages.impl.fixtures.aTimelineItemsFactoryCreator import io.element.android.features.messages.impl.pinned.PinnedEventsTimelineProvider import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.networkmonitor.api.NetworkMonitor @@ -318,7 +318,7 @@ class PinnedMessagesListPresenterTest { return PinnedMessagesListPresenter( navigator = navigator, room = room, - timelineItemsFactory = aTimelineItemsFactory(), + timelineItemsFactoryCreator = aTimelineItemsFactoryCreator(), timelineProvider = timelineProvider, snackbarDispatcher = SnackbarDispatcher(), actionListPresenterFactory = FakeActionListPresenter.Factory, 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 e90a781918..9df9dfc9f8 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 @@ -14,9 +14,8 @@ import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.messages.impl.FakeMessagesNavigator import io.element.android.features.messages.impl.fixtures.aMessageEvent -import io.element.android.features.messages.impl.fixtures.aTimelineItemsFactory +import io.element.android.features.messages.impl.fixtures.aTimelineItemsFactoryCreator import io.element.android.features.messages.impl.timeline.components.aCriticalShield -import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory import io.element.android.features.messages.impl.timeline.model.NewEventState import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.voicemessages.timeline.FakeRedactedVoiceMessageManager @@ -662,7 +661,6 @@ import kotlin.time.Duration.Companion.seconds liveTimeline = timeline, canUserSendMessageResult = { _, _ -> Result.success(true) } ), - timelineItemsFactory: TimelineItemsFactory = aTimelineItemsFactory(), redactedVoiceMessageManager: RedactedVoiceMessageManager = FakeRedactedVoiceMessageManager(), messagesNavigator: FakeMessagesNavigator = FakeMessagesNavigator(), endPollAction: EndPollAction = FakeEndPollAction(), @@ -671,7 +669,7 @@ import kotlin.time.Duration.Companion.seconds timelineItemIndexer: TimelineItemIndexer = TimelineItemIndexer(), ): TimelinePresenter { return TimelinePresenter( - timelineItemsFactory = timelineItemsFactory, + timelineItemsFactoryCreator = aTimelineItemsFactoryCreator(), room = room, dispatchers = testCoroutineDispatchers(), appScope = this,