timeline : makes typingNotification item part of the timelineItems.
This commit is contained in:
parent
f8fa218146
commit
1e4c30c569
21 changed files with 95 additions and 68 deletions
|
|
@ -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(
|
|||
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(
|
|||
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(
|
|||
userEventPermissions = userEventPermissions,
|
||||
voiceMessageComposerState = voiceMessageComposerState,
|
||||
timelineState = timelineState,
|
||||
typingNotificationState = typingNotificationState,
|
||||
actionListState = actionListState,
|
||||
customReactionState = customReactionState,
|
||||
reactionSummaryState = reactionSummaryState,
|
||||
|
|
|
|||
|
|
@ -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(
|
|||
val composerState: MessageComposerState,
|
||||
val voiceMessageComposerState: VoiceMessageComposerState,
|
||||
val timelineState: TimelineState,
|
||||
val typingNotificationState: TypingNotificationState,
|
||||
val actionListState: ActionListState,
|
||||
val customReactionState: CustomReactionState,
|
||||
val reactionSummaryState: ReactionSummaryState,
|
||||
|
|
|
|||
|
|
@ -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(
|
|||
userEventPermissions = userEventPermissions,
|
||||
composerState = composerState,
|
||||
voiceMessageComposerState = voiceMessageComposerState,
|
||||
typingNotificationState = aTypingNotificationState(),
|
||||
timelineState = timelineState,
|
||||
readReceiptBottomSheetState = readReceiptBottomSheetState,
|
||||
actionListState = actionListState,
|
||||
|
|
|
|||
|
|
@ -379,7 +379,6 @@ private fun MessagesViewContent(
|
|||
val scrollBehavior = PinnedMessagesBannerViewDefaults.rememberExitOnScrollBehavior()
|
||||
TimelineView(
|
||||
state = state.timelineState,
|
||||
typingNotificationState = state.typingNotificationState,
|
||||
onUserDataClick = onUserDataClick,
|
||||
onLinkClick = onLinkClick,
|
||||
onMessageClick = onMessageClick,
|
||||
|
|
|
|||
|
|
@ -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 {
|
|||
|
||||
@Binds
|
||||
fun bindResolveVerifiedUserSendFailurePresenter(presenter: ResolveVerifiedUserSendFailurePresenter): Presenter<ResolveVerifiedUserSendFailureState>
|
||||
|
||||
@Binds
|
||||
fun bindTypingNotificationPresenter(presenter: TypingNotificationPresenter): Presenter<TypingNotificationState>
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
|||
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(
|
|||
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,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
|||
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(
|
|||
.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(
|
|||
userHasPermissionToSendReaction = userHasPermissionToSendReaction,
|
||||
isCallOngoing = roomInfo?.hasRoomCall.orFalse(),
|
||||
pinnedEventIds = roomInfo?.pinnedEventIds.orEmpty(),
|
||||
typingNotificationState = typingNotificationState,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
|||
val userHasPermissionToSendMessage: Boolean,
|
||||
val userHasPermissionToSendReaction: Boolean,
|
||||
val isCallOngoing: Boolean,
|
||||
val pinnedEventIds: List<EventId>
|
||||
val pinnedEventIds: List<EventId>,
|
||||
val typingNotificationState: TypingNotificationState,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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(
|
|||
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(
|
|||
userHasPermissionToSendReaction = true,
|
||||
isCallOngoing = false,
|
||||
pinnedEventIds = pinnedEventIds,
|
||||
typingNotificationState = typingNotificationState,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
|||
@Composable
|
||||
fun TimelineView(
|
||||
state: TimelineState,
|
||||
typingNotificationState: TypingNotificationState,
|
||||
onUserDataClick: (UserId) -> Unit,
|
||||
onLinkClick: (String) -> Unit,
|
||||
onMessageClick: (TimelineItem.Event) -> Unit,
|
||||
|
|
@ -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(
|
|||
),
|
||||
focusedEventIndex = 0,
|
||||
),
|
||||
typingNotificationState = aTypingNotificationState(),
|
||||
onUserDataClick = {},
|
||||
onLinkClick = {},
|
||||
onMessageClick = {},
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ internal fun TimelineViewMessageShieldPreview() = ElementPreview {
|
|||
timelineItems = items.toImmutableList(),
|
||||
messageShield = messageShield,
|
||||
),
|
||||
typingNotificationState = aTypingNotificationState(),
|
||||
onUserDataClick = {},
|
||||
onLinkClick = {},
|
||||
onMessageClick = {},
|
||||
|
|
|
|||
|
|
@ -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(
|
|||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
|||
timestamp = inner.timestamp
|
||||
)
|
||||
is VirtualTimelineItem.LastForwardIndicator -> TimelineItemLastForwardIndicatorModel
|
||||
VirtualTimelineItem.TypingNotification -> TimelineItemTypingNotificationModel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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 = {},
|
||||
)
|
||||
}
|
||||
|
|
@ -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 {
|
|||
}
|
||||
}
|
||||
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 {
|
|||
composerPresenter = messageComposerPresenter,
|
||||
voiceMessageComposerPresenter = voiceMessageComposerPresenter,
|
||||
timelinePresenterFactory = timelinePresenterFactory,
|
||||
typingNotificationPresenter = typingNotificationPresenter,
|
||||
actionListPresenterFactory = FakeActionListPresenter.Factory,
|
||||
customReactionPresenter = customReactionPresenter,
|
||||
reactionSummaryPresenter = reactionSummaryPresenter,
|
||||
|
|
|
|||
|
|
@ -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
|
|||
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
|
|||
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
|
|||
timelineItemIndexer = timelineItemIndexer,
|
||||
timelineController = TimelineController(room),
|
||||
resolveVerifiedUserSendFailurePresenter = { aResolveVerifiedUserSendFailureState() },
|
||||
typingNotificationPresenter = { aTypingNotificationState() },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -155,7 +155,6 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setTimel
|
|||
setSafeContent {
|
||||
TimelineView(
|
||||
state = state,
|
||||
typingNotificationState = typingNotificationState,
|
||||
onUserDataClick = onUserDataClick,
|
||||
onLinkClick = onLinkClick,
|
||||
onMessageClick = onMessageClick,
|
||||
|
|
|
|||
|
|
@ -24,4 +24,7 @@ sealed interface VirtualTimelineItem {
|
|||
val direction: Timeline.PaginationDirection,
|
||||
val timestamp: Long,
|
||||
) : VirtualTimelineItem
|
||||
|
||||
data object TypingNotification : VirtualTimelineItem
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
|||
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(
|
|||
hasMoreToLoadForward = hasMoreToLoadForward
|
||||
)
|
||||
}
|
||||
.let { items ->
|
||||
typingNotificationPostProcessor.process(items = items)
|
||||
}
|
||||
// Keep lastForwardIndicatorsPostProcessor last
|
||||
.let { items ->
|
||||
lastForwardIndicatorsPostProcessor.process(
|
||||
|
|
|
|||
|
|
@ -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…
Add table
Add a link
Reference in a new issue