Browse Source

timeline : makes typingNotification item part of the timelineItems.

pull/3597/head
ganfra 2 weeks ago
parent
commit
1e4c30c569
  1. 4
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt
  2. 2
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt
  3. 2
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt
  4. 1
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt
  5. 6
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/di/MessagesModule.kt
  6. 9
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt
  7. 6
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt
  8. 4
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineState.kt
  9. 4
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt
  10. 10
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt
  11. 1
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewMessageShieldPreview.kt
  12. 8
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemVirtualRow.kt
  13. 2
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/virtual/TimelineItemVirtualFactory.kt
  14. 12
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/virtual/TimelineItemTypingNotificationModel.kt
  15. 35
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/MessagesViewWithTypingPreview.kt
  16. 7
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt
  17. 6
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt
  18. 1
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewTest.kt
  19. 3
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/virtual/VirtualTimelineItem.kt
  20. 5
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt
  21. 35
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TypingNotificationPostProcessor.kt

4
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt

@ -46,7 +46,6 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem @@ -46,7 +46,6 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent
import io.element.android.features.messages.impl.typing.TypingNotificationPresenter
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerPresenter
import io.element.android.features.networkmonitor.api.NetworkMonitor
import io.element.android.features.networkmonitor.api.NetworkStatus
@ -91,7 +90,6 @@ class MessagesPresenter @AssistedInject constructor( @@ -91,7 +90,6 @@ class MessagesPresenter @AssistedInject constructor(
private val composerPresenter: MessageComposerPresenter,
private val voiceMessageComposerPresenter: VoiceMessageComposerPresenter,
timelinePresenterFactory: TimelinePresenter.Factory,
private val typingNotificationPresenter: TypingNotificationPresenter,
private val actionListPresenterFactory: ActionListPresenter.Factory,
private val customReactionPresenter: CustomReactionPresenter,
private val reactionSummaryPresenter: ReactionSummaryPresenter,
@ -125,7 +123,6 @@ class MessagesPresenter @AssistedInject constructor( @@ -125,7 +123,6 @@ class MessagesPresenter @AssistedInject constructor(
val composerState = composerPresenter.present()
val voiceMessageComposerState = voiceMessageComposerPresenter.present()
val timelineState = timelinePresenter.present()
val typingNotificationState = typingNotificationPresenter.present()
val actionListState = actionListPresenter.present()
val customReactionState = customReactionPresenter.present()
val reactionSummaryState = reactionSummaryPresenter.present()
@ -216,7 +213,6 @@ class MessagesPresenter @AssistedInject constructor( @@ -216,7 +213,6 @@ class MessagesPresenter @AssistedInject constructor(
userEventPermissions = userEventPermissions,
voiceMessageComposerState = voiceMessageComposerState,
timelineState = timelineState,
typingNotificationState = typingNotificationState,
actionListState = actionListState,
customReactionState = customReactionState,
reactionSummaryState = reactionSummaryState,

2
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt

@ -15,7 +15,6 @@ import io.element.android.features.messages.impl.timeline.TimelineState @@ -15,7 +15,6 @@ 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
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetState
import io.element.android.features.messages.impl.typing.TypingNotificationState
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerState
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.designsystem.components.avatar.AvatarData
@ -33,7 +32,6 @@ data class MessagesState( @@ -33,7 +32,6 @@ data class MessagesState(
val composerState: MessageComposerState,
val voiceMessageComposerState: VoiceMessageComposerState,
val timelineState: TimelineState,
val typingNotificationState: TypingNotificationState,
val actionListState: ActionListState,
val customReactionState: CustomReactionState,
val reactionSummaryState: ReactionSummaryState,

2
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt

@ -26,7 +26,6 @@ import io.element.android.features.messages.impl.timeline.components.receipt.bot @@ -26,7 +26,6 @@ import io.element.android.features.messages.impl.timeline.components.receipt.bot
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetState
import io.element.android.features.messages.impl.timeline.model.TimelineItem
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
import io.element.android.features.messages.impl.typing.aTypingNotificationState
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerState
import io.element.android.features.messages.impl.voicemessages.composer.aVoiceMessageComposerState
import io.element.android.features.messages.impl.voicemessages.composer.aVoiceMessagePreviewState
@ -122,7 +121,6 @@ fun aMessagesState( @@ -122,7 +121,6 @@ fun aMessagesState(
userEventPermissions = userEventPermissions,
composerState = composerState,
voiceMessageComposerState = voiceMessageComposerState,
typingNotificationState = aTypingNotificationState(),
timelineState = timelineState,
readReceiptBottomSheetState = readReceiptBottomSheetState,
actionListState = actionListState,

1
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt

@ -379,7 +379,6 @@ private fun MessagesViewContent( @@ -379,7 +379,6 @@ private fun MessagesViewContent(
val scrollBehavior = PinnedMessagesBannerViewDefaults.rememberExitOnScrollBehavior()
TimelineView(
state = state.timelineState,
typingNotificationState = state.typingNotificationState,
onUserDataClick = onUserDataClick,
onLinkClick = onLinkClick,
onMessageClick = onMessageClick,

6
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/di/MessagesModule.kt

@ -14,6 +14,8 @@ import io.element.android.features.messages.impl.crypto.sendfailure.resolve.Reso @@ -14,6 +14,8 @@ import io.element.android.features.messages.impl.crypto.sendfailure.resolve.Reso
import io.element.android.features.messages.impl.crypto.sendfailure.resolve.ResolveVerifiedUserSendFailureState
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.typing.TypingNotificationPresenter
import io.element.android.features.messages.impl.typing.TypingNotificationState
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.di.RoomScope
@ -25,4 +27,8 @@ interface MessagesModule { @@ -25,4 +27,8 @@ interface MessagesModule {
@Binds
fun bindResolveVerifiedUserSendFailurePresenter(presenter: ResolveVerifiedUserSendFailurePresenter): Presenter<ResolveVerifiedUserSendFailureState>
@Binds
fun bindTypingNotificationPresenter(presenter: TypingNotificationPresenter): Presenter<TypingNotificationState>
}

9
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt

@ -30,6 +30,7 @@ import io.element.android.features.messages.impl.timeline.TimelineRoomInfo @@ -30,6 +30,7 @@ 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.features.messages.impl.typing.TypingNotificationState
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
@ -44,6 +45,7 @@ import io.element.android.libraries.ui.strings.CommonStrings @@ -44,6 +45,7 @@ import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.services.analytics.api.AnalyticsService
import io.element.android.services.analyticsproviders.api.trackers.captureInteraction
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flowOf
@ -87,7 +89,12 @@ class PinnedMessagesListPresenter @AssistedInject constructor( @@ -87,7 +89,12 @@ class PinnedMessagesListPresenter @AssistedInject constructor(
userHasPermissionToSendReaction = false,
isCallOngoing = false,
// don't compute this value or the pin icon will be shown
pinnedEventIds = emptyList()
pinnedEventIds = emptyList(),
typingNotificationState = TypingNotificationState(
renderTypingNotifications = false,
typingMembers = persistentListOf(),
reserveSpace = false,
)
)
}

6
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt

@ -28,6 +28,7 @@ import io.element.android.features.messages.impl.timeline.factories.TimelineItem @@ -28,6 +28,7 @@ import io.element.android.features.messages.impl.timeline.factories.TimelineItem
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.typing.TypingNotificationState
import io.element.android.features.messages.impl.voicemessages.timeline.RedactedVoiceMessageManager
import io.element.android.features.poll.api.actions.EndPollAction
import io.element.android.features.poll.api.actions.SendPollResponseAction
@ -70,6 +71,7 @@ class TimelinePresenter @AssistedInject constructor( @@ -70,6 +71,7 @@ class TimelinePresenter @AssistedInject constructor(
private val sessionPreferencesStore: SessionPreferencesStore,
private val timelineController: TimelineController,
private val resolveVerifiedUserSendFailurePresenter: Presenter<ResolveVerifiedUserSendFailureState>,
private val typingNotificationPresenter: Presenter<TypingNotificationState>,
) : Presenter<TimelineState> {
@AssistedFactory
interface Factory {
@ -225,7 +227,8 @@ class TimelinePresenter @AssistedInject constructor( @@ -225,7 +227,8 @@ class TimelinePresenter @AssistedInject constructor(
.launchIn(this)
}
val timelineRoomInfo by remember {
val typingNotificationState = typingNotificationPresenter.present()
val timelineRoomInfo by remember(typingNotificationState) {
derivedStateOf {
TimelineRoomInfo(
name = room.displayName,
@ -234,6 +237,7 @@ class TimelinePresenter @AssistedInject constructor( @@ -234,6 +237,7 @@ class TimelinePresenter @AssistedInject constructor(
userHasPermissionToSendReaction = userHasPermissionToSendReaction,
isCallOngoing = roomInfo?.hasRoomCall.orFalse(),
pinnedEventIds = roomInfo?.pinnedEventIds.orEmpty(),
typingNotificationState = typingNotificationState,
)
}
}

4
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineState.kt

@ -11,6 +11,7 @@ import androidx.compose.runtime.Immutable @@ -11,6 +11,7 @@ import androidx.compose.runtime.Immutable
import io.element.android.features.messages.impl.crypto.sendfailure.resolve.ResolveVerifiedUserSendFailureState
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.typing.TypingNotificationState
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield
import kotlinx.collections.immutable.ImmutableList
@ -67,5 +68,6 @@ data class TimelineRoomInfo( @@ -67,5 +68,6 @@ data class TimelineRoomInfo(
val userHasPermissionToSendMessage: Boolean,
val userHasPermissionToSendReaction: Boolean,
val isCallOngoing: Boolean,
val pinnedEventIds: List<EventId>
val pinnedEventIds: List<EventId>,
val typingNotificationState: TypingNotificationState,
)

4
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt

@ -21,6 +21,8 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt @@ -21,6 +21,8 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemStateEventContent
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
import io.element.android.features.messages.impl.timeline.model.virtual.aTimelineItemDaySeparatorModel
import io.element.android.features.messages.impl.typing.TypingNotificationState
import io.element.android.features.messages.impl.typing.aTypingNotificationState
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.matrix.api.core.EventId
@ -241,6 +243,7 @@ internal fun aTimelineRoomInfo( @@ -241,6 +243,7 @@ internal fun aTimelineRoomInfo(
isDm: Boolean = false,
userHasPermissionToSendMessage: Boolean = true,
pinnedEventIds: List<EventId> = emptyList(),
typingNotificationState: TypingNotificationState = aTypingNotificationState(),
) = TimelineRoomInfo(
isDm = isDm,
name = name,
@ -248,4 +251,5 @@ internal fun aTimelineRoomInfo( @@ -248,4 +251,5 @@ internal fun aTimelineRoomInfo(
userHasPermissionToSendReaction = true,
isCallOngoing = false,
pinnedEventIds = pinnedEventIds,
typingNotificationState = typingNotificationState,
)

10
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt

@ -55,9 +55,6 @@ import io.element.android.features.messages.impl.timeline.model.NewEventState @@ -55,9 +55,6 @@ 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.timeline.model.event.TimelineItemEventContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContentProvider
import io.element.android.features.messages.impl.typing.TypingNotificationState
import io.element.android.features.messages.impl.typing.TypingNotificationView
import io.element.android.features.messages.impl.typing.aTypingNotificationState
import io.element.android.libraries.designsystem.components.dialogs.AlertDialog
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
@ -73,7 +70,6 @@ import kotlin.math.abs @@ -73,7 +70,6 @@ import kotlin.math.abs
@Composable
fun TimelineView(
state: TimelineState,
typingNotificationState: TypingNotificationState,
onUserDataClick: (UserId) -> Unit,
onLinkClick: (String) -> Unit,
onMessageClick: (TimelineItem.Event) -> Unit,
@ -131,11 +127,6 @@ fun TimelineView( @@ -131,11 +127,6 @@ fun TimelineView(
reverseLayout = useReverseLayout,
contentPadding = PaddingValues(vertical = 8.dp),
) {
if (state.isLive) {
item {
TypingNotificationView(state = typingNotificationState)
}
}
items(
items = state.timelineItems,
contentType = { timelineItem -> timelineItem.contentType() },
@ -323,7 +314,6 @@ internal fun TimelineViewPreview( @@ -323,7 +314,6 @@ internal fun TimelineViewPreview(
),
focusedEventIndex = 0,
),
typingNotificationState = aTypingNotificationState(),
onUserDataClick = {},
onLinkClick = {},
onMessageClick = {},

1
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewMessageShieldPreview.kt

@ -36,7 +36,6 @@ internal fun TimelineViewMessageShieldPreview() = ElementPreview { @@ -36,7 +36,6 @@ internal fun TimelineViewMessageShieldPreview() = ElementPreview {
timelineItems = items.toImmutableList(),
messageShield = messageShield,
),
typingNotificationState = aTypingNotificationState(),
onUserDataClick = {},
onLinkClick = {},
onMessageClick = {},

8
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemVirtualRow.kt

@ -26,6 +26,8 @@ import io.element.android.features.messages.impl.timeline.model.virtual.Timeline @@ -26,6 +26,8 @@ import io.element.android.features.messages.impl.timeline.model.virtual.Timeline
import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemLoadingIndicatorModel
import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemReadMarkerModel
import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemRoomBeginningModel
import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemTypingNotificationModel
import io.element.android.features.messages.impl.typing.TypingNotificationView
@Composable
fun TimelineItemVirtualRow(
@ -46,9 +48,15 @@ fun TimelineItemVirtualRow( @@ -46,9 +48,15 @@ fun TimelineItemVirtualRow(
latestEventSink(TimelineEvents.LoadMore(virtual.model.direction))
}
}
// Empty model trick to avoid timeline jumping during forward pagination.
is TimelineItemLastForwardIndicatorModel -> {
Spacer(modifier = Modifier)
}
is TimelineItemTypingNotificationModel -> {
TypingNotificationView(
state = timelineRoomInfo.typingNotificationState,
)
}
}
}
}

2
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/virtual/TimelineItemVirtualFactory.kt

@ -12,6 +12,7 @@ import io.element.android.features.messages.impl.timeline.model.virtual.Timeline @@ -12,6 +12,7 @@ import io.element.android.features.messages.impl.timeline.model.virtual.Timeline
import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemLoadingIndicatorModel
import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemReadMarkerModel
import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemRoomBeginningModel
import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemTypingNotificationModel
import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemVirtualModel
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem
@ -39,6 +40,7 @@ class TimelineItemVirtualFactory @Inject constructor( @@ -39,6 +40,7 @@ class TimelineItemVirtualFactory @Inject constructor(
timestamp = inner.timestamp
)
is VirtualTimelineItem.LastForwardIndicator -> TimelineItemLastForwardIndicatorModel
VirtualTimelineItem.TypingNotification -> TimelineItemTypingNotificationModel
}
}
}

12
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/virtual/TimelineItemTypingNotificationModel.kt

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
/*
* 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.model.virtual
data object TimelineItemTypingNotificationModel : TimelineItemVirtualModel {
override val type: String = "TimelineItemTypingNotificationModel"
}

35
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/typing/MessagesViewWithTypingPreview.kt

@ -1,35 +0,0 @@ @@ -1,35 +0,0 @@
/*
* 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.typing
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.PreviewParameter
import io.element.android.features.messages.impl.MessagesView
import io.element.android.features.messages.impl.aMessagesState
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
@PreviewsDayNight
@Composable
internal fun MessagesViewWithTypingPreview(
@PreviewParameter(TypingNotificationStateForMessagesProvider::class) typingState: TypingNotificationState
) = ElementPreview {
MessagesView(
state = aMessagesState().copy(typingNotificationState = typingState),
onBackClick = {},
onRoomDetailsClick = {},
onEventClick = { false },
onUserDataClick = {},
onLinkClick = {},
onPreviewAttachments = {},
onSendLocationClick = {},
onCreatePollClick = {},
onJoinCallClick = {},
onViewAllPinnedMessagesClick = {},
)
}

7
features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt

@ -40,7 +40,6 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt @@ -40,7 +40,6 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemPollContent
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
import io.element.android.features.messages.impl.typing.TypingNotificationPresenter
import io.element.android.features.messages.impl.utils.FakeTextPillificationHelper
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerPlayer
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerPresenter
@ -1055,11 +1054,6 @@ class MessagesPresenterTest { @@ -1055,11 +1054,6 @@ class MessagesPresenterTest {
}
}
val featureFlagService = FakeFeatureFlagService()
val typingNotificationPresenter = TypingNotificationPresenter(
room = matrixRoom,
sessionPreferencesStore = sessionPreferencesStore,
)
val readReceiptBottomSheetPresenter = ReadReceiptBottomSheetPresenter()
val customReactionPresenter = CustomReactionPresenter(emojibaseProvider = FakeEmojibaseProvider())
val reactionSummaryPresenter = ReactionSummaryPresenter(room = matrixRoom)
@ -1069,7 +1063,6 @@ class MessagesPresenterTest { @@ -1069,7 +1063,6 @@ class MessagesPresenterTest {
composerPresenter = messageComposerPresenter,
voiceMessageComposerPresenter = voiceMessageComposerPresenter,
timelinePresenterFactory = timelinePresenterFactory,
typingNotificationPresenter = typingNotificationPresenter,
actionListPresenterFactory = FakeActionListPresenter.Factory,
customReactionPresenter = customReactionPresenter,
reactionSummaryPresenter = reactionSummaryPresenter,

6
features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt

@ -19,6 +19,7 @@ import io.element.android.features.messages.impl.fixtures.aTimelineItemsFactoryC @@ -19,6 +19,7 @@ import io.element.android.features.messages.impl.fixtures.aTimelineItemsFactoryC
import io.element.android.features.messages.impl.timeline.components.aCriticalShield
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.typing.aTypingNotificationState
import io.element.android.features.messages.impl.voicemessages.timeline.FakeRedactedVoiceMessageManager
import io.element.android.features.messages.impl.voicemessages.timeline.RedactedVoiceMessageManager
import io.element.android.features.messages.impl.voicemessages.timeline.aRedactedMatrixTimeline
@ -503,7 +504,7 @@ import kotlin.time.Duration.Companion.seconds @@ -503,7 +504,7 @@ import kotlin.time.Duration.Companion.seconds
assertThat(state.timelineItems).isNotEmpty()
}
initialState.eventSink.invoke(TimelineEvents.JumpToLive)
skipItems(1)
skipItems(2)
awaitItem().also { state ->
// Event stays focused
assertThat(state.focusedEventId).isEqualTo(AN_EVENT_ID)
@ -670,7 +671,7 @@ import kotlin.time.Duration.Companion.seconds @@ -670,7 +671,7 @@ import kotlin.time.Duration.Companion.seconds
timelineItemIndexer: TimelineItemIndexer = TimelineItemIndexer(),
): TimelinePresenter {
return TimelinePresenter(
timelineItemsFactoryCreator = aTimelineItemsFactoryCreator(),
timelineItemsFactoryCreator = aTimelineItemsFactoryCreator(timelineItemIndexer),
room = room,
dispatchers = testCoroutineDispatchers(),
appScope = this,
@ -682,6 +683,7 @@ import kotlin.time.Duration.Companion.seconds @@ -682,6 +683,7 @@ import kotlin.time.Duration.Companion.seconds
timelineItemIndexer = timelineItemIndexer,
timelineController = TimelineController(room),
resolveVerifiedUserSendFailurePresenter = { aResolveVerifiedUserSendFailureState() },
typingNotificationPresenter = { aTypingNotificationState() },
)
}
}

1
features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewTest.kt

@ -155,7 +155,6 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setTimel @@ -155,7 +155,6 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setTimel
setSafeContent {
TimelineView(
state = state,
typingNotificationState = typingNotificationState,
onUserDataClick = onUserDataClick,
onLinkClick = onLinkClick,
onMessageClick = onMessageClick,

3
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/virtual/VirtualTimelineItem.kt

@ -24,4 +24,7 @@ sealed interface VirtualTimelineItem { @@ -24,4 +24,7 @@ sealed interface VirtualTimelineItem {
val direction: Timeline.PaginationDirection,
val timestamp: Long,
) : VirtualTimelineItem
data object TypingNotification : VirtualTimelineItem
}

5
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt

@ -40,6 +40,7 @@ import io.element.android.libraries.matrix.impl.timeline.item.virtual.VirtualTim @@ -40,6 +40,7 @@ import io.element.android.libraries.matrix.impl.timeline.item.virtual.VirtualTim
import io.element.android.libraries.matrix.impl.timeline.postprocessor.LastForwardIndicatorsPostProcessor
import io.element.android.libraries.matrix.impl.timeline.postprocessor.LoadingIndicatorsPostProcessor
import io.element.android.libraries.matrix.impl.timeline.postprocessor.RoomBeginningPostProcessor
import io.element.android.libraries.matrix.impl.timeline.postprocessor.TypingNotificationPostProcessor
import io.element.android.libraries.matrix.impl.timeline.reply.InReplyToMapper
import io.element.android.libraries.matrix.impl.util.MessageEventContent
import io.element.android.services.toolbox.api.systemclock.SystemClock
@ -121,6 +122,7 @@ class RustTimeline( @@ -121,6 +122,7 @@ class RustTimeline(
private val roomBeginningPostProcessor = RoomBeginningPostProcessor(mode)
private val loadingIndicatorsPostProcessor = LoadingIndicatorsPostProcessor(systemClock)
private val lastForwardIndicatorsPostProcessor = LastForwardIndicatorsPostProcessor(mode)
private val typingNotificationPostProcessor = TypingNotificationPostProcessor(mode)
private val backPaginationStatus = MutableStateFlow(
Timeline.PaginationStatus(isPaginating = false, hasMoreToLoad = mode != Timeline.Mode.PINNED_EVENTS)
@ -235,6 +237,9 @@ class RustTimeline( @@ -235,6 +237,9 @@ class RustTimeline(
hasMoreToLoadForward = hasMoreToLoadForward
)
}
.let { items ->
typingNotificationPostProcessor.process(items = items)
}
// Keep lastForwardIndicatorsPostProcessor last
.let { items ->
lastForwardIndicatorsPostProcessor.process(

35
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TypingNotificationPostProcessor.kt

@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
/*
* 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.libraries.matrix.impl.timeline.postprocessor
import io.element.android.libraries.matrix.api.core.UniqueId
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem
/**
* This post processor is responsible for adding a typing notification item to the timeline items when the timeline is in live mode.
*/
class TypingNotificationPostProcessor(private val mode: Timeline.Mode) {
fun process(items: List<MatrixTimelineItem>): List<MatrixTimelineItem> {
return if (mode == Timeline.Mode.LIVE) {
buildList {
addAll(items)
add(
MatrixTimelineItem.Virtual(
uniqueId = UniqueId("TypingNotification"),
virtual = VirtualTimelineItem.TypingNotification
)
)
}
} else {
items
}
}
}
Loading…
Cancel
Save