Format and cleanup files

This commit is contained in:
Benoit Marty 2024-04-26 13:57:08 +02:00
parent d20605b8be
commit 21ddeb00fe
27 changed files with 235 additions and 111 deletions

View file

@ -99,7 +99,7 @@ class MessagesFlowNode @AssistedInject constructor(
data object Empty : NavTarget data object Empty : NavTarget
@Parcelize @Parcelize
data object Messages: NavTarget data object Messages : NavTarget
@Parcelize @Parcelize
data class MediaViewer( data class MediaViewer(

View file

@ -29,7 +29,6 @@ import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.timeline.TimelineProvider import io.element.android.libraries.matrix.api.timeline.TimelineProvider
import io.element.android.libraries.matrix.api.timeline.getActiveTimeline import io.element.android.libraries.matrix.api.timeline.getActiveTimeline
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList

View file

@ -53,7 +53,6 @@ import kotlin.coroutines.cancellation.CancellationException
class TimelineController @Inject constructor( class TimelineController @Inject constructor(
private val room: MatrixRoom, private val room: MatrixRoom,
) : Closeable, TimelineProvider { ) : Closeable, TimelineProvider {
private val coroutineScope = CoroutineScope(SupervisorJob()) private val coroutineScope = CoroutineScope(SupervisorJob())
private val liveTimeline = MutableStateFlow(room.liveTimeline) private val liveTimeline = MutableStateFlow(room.liveTimeline)

View file

@ -22,7 +22,7 @@ import io.element.android.libraries.matrix.api.timeline.Timeline
sealed interface TimelineEvents { sealed interface TimelineEvents {
data class OnScrollFinished(val firstIndex: Int) : TimelineEvents data class OnScrollFinished(val firstIndex: Int) : TimelineEvents
data class FocusOnEvent(val eventId: EventId) : TimelineEvents data class FocusOnEvent(val eventId: EventId) : TimelineEvents
data object ClearFocusRequestState: TimelineEvents data object ClearFocusRequestState : TimelineEvents
data object JumpToLive : TimelineEvents data object JumpToLive : TimelineEvents
/** /**

View file

@ -25,7 +25,6 @@ import javax.inject.Inject
@SingleIn(RoomScope::class) @SingleIn(RoomScope::class)
class TimelineItemIndexer @Inject constructor() { class TimelineItemIndexer @Inject constructor() {
private val timelineEventsIndexes = mutableMapOf<EventId, Int>() private val timelineEventsIndexes = mutableMapOf<EventId, Int>()
fun isKnown(eventId: EventId): Boolean { fun isKnown(eventId: EventId): Boolean {

View file

@ -29,18 +29,18 @@ data class TimelineState(
val renderReadReceipts: Boolean, val renderReadReceipts: Boolean,
val newEventState: NewEventState, val newEventState: NewEventState,
val isLive: Boolean, val isLive: Boolean,
val focusedEventId : EventId?, val focusedEventId: EventId?,
val focusRequestState: FocusRequestState, val focusRequestState: FocusRequestState,
val eventSink: (TimelineEvents) -> Unit, val eventSink: (TimelineEvents) -> Unit,
){ ) {
val hasAnyEvent = timelineItems.any { it is TimelineItem.Event } val hasAnyEvent = timelineItems.any { it is TimelineItem.Event }
} }
sealed interface FocusRequestState { sealed interface FocusRequestState {
data object None : FocusRequestState data object None : FocusRequestState
data class Cached(val index: Int): FocusRequestState data class Cached(val index: Int) : FocusRequestState
data object Fetching : FocusRequestState data object Fetching : FocusRequestState
data object Fetched: FocusRequestState data object Fetched : FocusRequestState
data class Failure(val throwable: Throwable) : FocusRequestState data class Failure(val throwable: Throwable) : FocusRequestState
} }

View file

@ -71,7 +71,6 @@ fun aPaginationStatus(
) )
} }
internal fun aTimelineItemList(content: TimelineItemEventContent): ImmutableList<TimelineItem> { internal fun aTimelineItemList(content: TimelineItemEventContent): ImmutableList<TimelineItem> {
return persistentListOf( return persistentListOf(
// 3 items (First Middle Last) with isMine = false // 3 items (First Middle Last) with isMine = false

View file

@ -93,7 +93,6 @@ fun TimelineView(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
forceJumpToBottomVisibility: Boolean = false forceJumpToBottomVisibility: Boolean = false
) { ) {
fun clearFocusRequestState() { fun clearFocusRequestState() {
state.eventSink(TimelineEvents.ClearFocusRequestState) state.eventSink(TimelineEvents.ClearFocusRequestState)
} }
@ -124,8 +123,7 @@ fun TimelineView(
reverseLayout = useReverseLayout, reverseLayout = useReverseLayout,
contentPadding = PaddingValues(vertical = 8.dp), contentPadding = PaddingValues(vertical = 8.dp),
) { ) {
if (state.isLive) {
if(state.isLive) {
item { item {
TypingNotificationView(state = typingNotificationState) TypingNotificationView(state = typingNotificationState)
} }

View file

@ -57,7 +57,6 @@ internal fun TimelineItemRow(
eventSink: (TimelineEvents.EventFromTimelineItem) -> Unit, eventSink: (TimelineEvents.EventFromTimelineItem) -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val backgroundModifier = if (timelineItem.isEvent(focusedEventId)) { val backgroundModifier = if (timelineItem.isEvent(focusedEventId)) {
Modifier.focusedEvent() Modifier.focusedEvent()
} else { } else {

View file

@ -56,7 +56,6 @@ internal fun TimelineLoadingMoreIndicator(
) )
} }
} }
} }
} }

View file

@ -34,7 +34,6 @@ import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList import kotlinx.collections.immutable.toPersistentList
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext

View file

@ -16,6 +16,6 @@
package io.element.android.features.messages.impl.timeline.model.virtual package io.element.android.features.messages.impl.timeline.model.virtual
data object TimelineItemLastForwardIndicatorModel: TimelineItemVirtualModel { data object TimelineItemLastForwardIndicatorModel : TimelineItemVirtualModel {
override val type: String = "TimelineItemLastForwardIndicatorModel" override val type: String = "TimelineItemLastForwardIndicatorModel"
} }

View file

@ -365,7 +365,7 @@ class MessageComposerPresenterTest {
@Test @Test
fun `present - reply message`() = runTest { fun `present - reply message`() = runTest {
val replyMessageLambda = lambdaRecorder {_: EventId, _: String, _: String?, _:List<Mention> -> val replyMessageLambda = lambdaRecorder { _: EventId, _: String, _: String?, _: List<Mention> ->
Result.success(Unit) Result.success(Unit)
} }
val timeline = FakeTimeline().apply { val timeline = FakeTimeline().apply {
@ -396,7 +396,7 @@ class MessageComposerPresenterTest {
assert(replyMessageLambda) assert(replyMessageLambda)
.isCalledOnce() .isCalledOnce()
.with(any(),value(A_REPLY),value(A_REPLY),any()) .with(any(), value(A_REPLY), value(A_REPLY), any())
assertThat(analyticsService.capturedEvents).containsExactly( assertThat(analyticsService.capturedEvents).containsExactly(
Composer( Composer(
@ -873,7 +873,7 @@ class MessageComposerPresenterTest {
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
@Test @Test
fun `present - send messages with intentional mentions`() = runTest { fun `present - send messages with intentional mentions`() = runTest {
val replyMessageLambda = lambdaRecorder {_: EventId, _: String, _: String?, _:List<Mention> -> val replyMessageLambda = lambdaRecorder { _: EventId, _: String, _: String?, _: List<Mention> ->
Result.success(Unit) Result.success(Unit)
} }
val editMessageLambda = lambdaRecorder { _: EventId?, _: TransactionId?, _: String, _: String?, _: List<Mention> -> val editMessageLambda = lambdaRecorder { _: EventId?, _: TransactionId?, _: String, _: String?, _: List<Mention> ->
@ -940,7 +940,6 @@ class MessageComposerPresenterTest {
.isCalledOnce() .isCalledOnce()
.with(any(), any(), any(), any(), value(listOf(Mention.User(A_USER_ID_3)))) .with(any(), any(), any(), any(), value(listOf(Mention.User(A_USER_ID_3))))
skipItems(1) skipItems(1)
} }
} }

View file

@ -33,7 +33,6 @@ import io.element.android.features.poll.api.actions.EndPollAction
import io.element.android.features.poll.api.actions.SendPollResponseAction import io.element.android.features.poll.api.actions.SendPollResponseAction
import io.element.android.features.poll.test.actions.FakeEndPollAction import io.element.android.features.poll.test.actions.FakeEndPollAction
import io.element.android.features.poll.test.actions.FakeSendPollResponseAction import io.element.android.features.poll.test.actions.FakeSendPollResponseAction
import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.featureflag.test.InMemorySessionPreferencesStore import io.element.android.libraries.featureflag.test.InMemorySessionPreferencesStore
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
@ -69,7 +68,6 @@ import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.getAndUpdate import kotlinx.coroutines.flow.getAndUpdate
import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest

View file

@ -32,11 +32,9 @@ import io.element.android.features.poll.impl.history.model.PollHistoryFilter
import io.element.android.features.poll.impl.history.model.PollHistoryItems import io.element.android.features.poll.impl.history.model.PollHistoryItems
import io.element.android.features.poll.impl.history.model.PollHistoryItemsFactory import io.element.android.features.poll.impl.history.model.PollHistoryItemsFactory
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.matrix.api.timeline.TimelineProvider import io.element.android.libraries.matrix.api.timeline.TimelineProvider
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.flatMapConcat
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject

View file

@ -61,9 +61,10 @@ class PollHistoryPresenterTest {
private val timeline = FakeTimeline( private val timeline = FakeTimeline(
timelineItems = aPollTimelineItems( timelineItems = aPollTimelineItems(
mapOf( mapOf(
AN_EVENT_ID to anOngoingPollContent(), AN_EVENT_ID to anOngoingPollContent(),
AN_EVENT_ID_2 to anEndedPollContent() AN_EVENT_ID_2 to anEndedPollContent()
)), )
),
backwardPaginationStatus = backwardPaginationStatus backwardPaginationStatus = backwardPaginationStatus
) )
private val room = FakeMatrixRoom( private val room = FakeMatrixRoom(
@ -136,7 +137,7 @@ class PollHistoryPresenterTest {
@Test @Test
fun `present - load more scenario`() = runTest { fun `present - load more scenario`() = runTest {
val paginateLambda = lambdaRecorder{ _: Timeline.PaginationDirection -> val paginateLambda = lambdaRecorder { _: Timeline.PaginationDirection ->
Result.success(false) Result.success(false)
} }
timeline.apply { timeline.apply {

View file

@ -153,7 +153,6 @@ val SemanticColors.bigCheckmarkBorderColor
val SemanticColors.highlightedMessageBackgroundColor val SemanticColors.highlightedMessageBackgroundColor
get() = if (isLight) LightColorTokens.colorGreen300 else DarkColorTokens.colorGreen300 get() = if (isLight) LightColorTokens.colorGreen300 else DarkColorTokens.colorGreen300
@PreviewsDayNight @PreviewsDayNight
@Composable @Composable
internal fun ColorAliasesPreview() = ElementPreview { internal fun ColorAliasesPreview() = ElementPreview {

View file

@ -32,9 +32,8 @@ import io.element.android.libraries.matrix.api.poll.PollKind
import io.element.android.libraries.matrix.api.room.location.AssetType import io.element.android.libraries.matrix.api.room.location.AssetType
import io.element.android.libraries.matrix.api.room.powerlevels.MatrixRoomPowerLevels import io.element.android.libraries.matrix.api.room.powerlevels.MatrixRoomPowerLevels
import io.element.android.libraries.matrix.api.room.powerlevels.UserRoleChange import io.element.android.libraries.matrix.api.room.powerlevels.UserRoleChange
import io.element.android.libraries.matrix.api.timeline.TimelineProvider
import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.api.timeline.ReceiptType
import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver
import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow

View file

@ -33,7 +33,6 @@ import kotlinx.coroutines.flow.StateFlow
import java.io.File import java.io.File
interface Timeline : AutoCloseable { interface Timeline : AutoCloseable {
data class PaginationStatus( data class PaginationStatus(
val isPaginating: Boolean, val isPaginating: Boolean,
val hasMoreToLoad: Boolean, val hasMoreToLoad: Boolean,
@ -52,7 +51,6 @@ interface Timeline : AutoCloseable {
fun paginationStatus(direction: PaginationDirection): StateFlow<PaginationStatus> fun paginationStatus(direction: PaginationDirection): StateFlow<PaginationStatus>
val timelineItems: Flow<List<MatrixTimelineItem>> val timelineItems: Flow<List<MatrixTimelineItem>>
suspend fun sendMessage(body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit> suspend fun sendMessage(body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit>
suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit> suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit>
@ -164,6 +162,4 @@ interface Timeline : AutoCloseable {
waveform: List<Float>, waveform: List<Float>,
progressCallback: ProgressCallback? progressCallback: ProgressCallback?
): Result<MediaUploadHandler> ): Result<MediaUploadHandler>
} }

View file

@ -19,11 +19,9 @@ package io.element.android.libraries.matrix.api.timeline
import com.squareup.anvil.annotations.ContributesBinding import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.di.RoomScope import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoom
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOf
import javax.inject.Inject import javax.inject.Inject
/** /**
@ -45,4 +43,3 @@ class LiveTimelineProvider @Inject constructor(
) : TimelineProvider { ) : TimelineProvider {
override fun activeTimelineFlow(): StateFlow<Timeline> = MutableStateFlow(room.liveTimeline) override fun activeTimelineFlow(): StateFlow<Timeline> = MutableStateFlow(room.liveTimeline)
} }

View file

@ -27,12 +27,12 @@ sealed interface VirtualTimelineItem {
data object EncryptedHistoryBanner : VirtualTimelineItem data object EncryptedHistoryBanner : VirtualTimelineItem
data object RoomBeginning: VirtualTimelineItem data object RoomBeginning : VirtualTimelineItem
data object LastForwardIndicator: VirtualTimelineItem data object LastForwardIndicator : VirtualTimelineItem
data class LoadingIndicator( data class LoadingIndicator(
val direction: Timeline.PaginationDirection, val direction: Timeline.PaginationDirection,
val timestamp: Long, val timestamp: Long,
): VirtualTimelineItem ) : VirtualTimelineItem
} }

View file

@ -47,7 +47,6 @@ import io.element.android.libraries.matrix.api.timeline.ReceiptType
import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver
import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings
import io.element.android.libraries.matrix.impl.media.MediaUploadHandlerImpl
import io.element.android.libraries.matrix.impl.notificationsettings.RustNotificationSettingsService import io.element.android.libraries.matrix.impl.notificationsettings.RustNotificationSettingsService
import io.element.android.libraries.matrix.impl.room.member.RoomMemberListFetcher import io.element.android.libraries.matrix.impl.room.member.RoomMemberListFetcher
import io.element.android.libraries.matrix.impl.room.member.RoomMemberMapper import io.element.android.libraries.matrix.impl.room.member.RoomMemberMapper
@ -78,14 +77,10 @@ import kotlinx.coroutines.withContext
import org.matrix.rustcomponents.sdk.RoomInfo import org.matrix.rustcomponents.sdk.RoomInfo
import org.matrix.rustcomponents.sdk.RoomInfoListener import org.matrix.rustcomponents.sdk.RoomInfoListener
import org.matrix.rustcomponents.sdk.RoomListItem import org.matrix.rustcomponents.sdk.RoomListItem
import org.matrix.rustcomponents.sdk.RoomMessageEventContentWithoutRelation
import org.matrix.rustcomponents.sdk.SendAttachmentJoinHandle
import org.matrix.rustcomponents.sdk.TypingNotificationsListener import org.matrix.rustcomponents.sdk.TypingNotificationsListener
import org.matrix.rustcomponents.sdk.UserPowerLevelUpdate import org.matrix.rustcomponents.sdk.UserPowerLevelUpdate
import org.matrix.rustcomponents.sdk.WidgetCapabilities import org.matrix.rustcomponents.sdk.WidgetCapabilities
import org.matrix.rustcomponents.sdk.WidgetCapabilitiesProvider import org.matrix.rustcomponents.sdk.WidgetCapabilitiesProvider
import org.matrix.rustcomponents.sdk.messageEventContentFromHtml
import org.matrix.rustcomponents.sdk.messageEventContentFromMarkdown
import org.matrix.rustcomponents.sdk.use import org.matrix.rustcomponents.sdk.use
import uniffi.matrix_sdk.RoomPowerLevelChanges import uniffi.matrix_sdk.RoomPowerLevelChanges
import java.io.File import java.io.File
@ -151,7 +146,7 @@ class RustMatrixRoom(
private val _roomNotificationSettingsStateFlow = MutableStateFlow<MatrixRoomNotificationSettingsState>(MatrixRoomNotificationSettingsState.Unknown) private val _roomNotificationSettingsStateFlow = MutableStateFlow<MatrixRoomNotificationSettingsState>(MatrixRoomNotificationSettingsState.Unknown)
override val roomNotificationSettingsStateFlow: StateFlow<MatrixRoomNotificationSettingsState> = _roomNotificationSettingsStateFlow override val roomNotificationSettingsStateFlow: StateFlow<MatrixRoomNotificationSettingsState> = _roomNotificationSettingsStateFlow
override val liveTimeline = createTimeline(innerTimeline, isLive = true){ override val liveTimeline = createTimeline(innerTimeline, isLive = true) {
_syncUpdateFlow.value = systemClock.epochMillis() _syncUpdateFlow.value = systemClock.epochMillis()
} }
@ -179,7 +174,7 @@ class RustMatrixRoom(
eventId = eventId.value, eventId = eventId.value,
numContextEvents = 50u, numContextEvents = 50u,
internalIdPrefix = "focus_$eventId", internalIdPrefix = "focus_$eventId",
).let {inner -> ).let { inner ->
createTimeline(inner, isLive = false) createTimeline(inner, isLive = false)
} }
} }
@ -322,7 +317,7 @@ class RustMatrixRoom(
} }
} }
override suspend fun sendMessage(body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit> { override suspend fun sendMessage(body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit> {
return liveTimeline.sendMessage(body, htmlBody, mentions) return liveTimeline.sendMessage(body, htmlBody, mentions)
} }
@ -340,7 +335,7 @@ class RustMatrixRoom(
return liveTimeline.enterSpecialMode(eventId) return liveTimeline.enterSpecialMode(eventId)
} }
override suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit>{ override suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit> {
return liveTimeline.replyMessage(eventId, body, htmlBody, mentions) return liveTimeline.replyMessage(eventId, body, htmlBody, mentions)
} }
@ -446,11 +441,11 @@ class RustMatrixRoom(
return liveTimeline.sendFile(file, fileInfo, progressCallback) return liveTimeline.sendFile(file, fileInfo, progressCallback)
} }
override suspend fun toggleReaction(emoji: String, eventId: EventId): Result<Unit>{ override suspend fun toggleReaction(emoji: String, eventId: EventId): Result<Unit> {
return liveTimeline.toggleReaction(emoji, eventId) return liveTimeline.toggleReaction(emoji, eventId)
} }
override suspend fun forwardEvent(eventId: EventId, roomIds: List<RoomId>): Result<Unit>{ override suspend fun forwardEvent(eventId: EventId, roomIds: List<RoomId>): Result<Unit> {
return liveTimeline.forwardEvent(eventId, roomIds) return liveTimeline.forwardEvent(eventId, roomIds)
} }
@ -458,7 +453,7 @@ class RustMatrixRoom(
return liveTimeline.retrySendMessage(transactionId) return liveTimeline.retrySendMessage(transactionId)
} }
override suspend fun cancelSend(transactionId: TransactionId): Result<Unit>{ override suspend fun cancelSend(transactionId: TransactionId): Result<Unit> {
return liveTimeline.cancelSend(transactionId) return liveTimeline.cancelSend(transactionId)
} }
@ -563,14 +558,14 @@ class RustMatrixRoom(
override suspend fun sendPollResponse( override suspend fun sendPollResponse(
pollStartId: EventId, pollStartId: EventId,
answers: List<String> answers: List<String>
): Result<Unit> { ): Result<Unit> {
return liveTimeline.sendPollResponse(pollStartId, answers) return liveTimeline.sendPollResponse(pollStartId, answers)
} }
override suspend fun endPoll( override suspend fun endPoll(
pollStartId: EventId, pollStartId: EventId,
text: String text: String
): Result<Unit> { ): Result<Unit> {
return liveTimeline.endPoll(pollStartId, text) return liveTimeline.endPoll(pollStartId, text)
} }
@ -579,7 +574,7 @@ class RustMatrixRoom(
audioInfo: AudioInfo, audioInfo: AudioInfo,
waveform: List<Float>, waveform: List<Float>,
progressCallback: ProgressCallback?, progressCallback: ProgressCallback?,
): Result<MediaUploadHandler>{ ): Result<MediaUploadHandler> {
return liveTimeline.sendVoiceMessage(file, audioInfo, waveform, progressCallback) return liveTimeline.sendVoiceMessage(file, audioInfo, waveform, progressCallback)
} }
@ -634,5 +629,4 @@ class RustMatrixRoom(
inner = timeline, inner = timeline,
) )
} }
} }

View file

@ -89,17 +89,16 @@ private const val PAGINATION_SIZE = 50
class RustTimeline( class RustTimeline(
private val inner: InnerTimeline, private val inner: InnerTimeline,
private val isLive: Boolean, isLive: Boolean,
private val systemClock: SystemClock, systemClock: SystemClock,
private val roomCoroutineScope: CoroutineScope, roomCoroutineScope: CoroutineScope,
private val isKeyBackupEnabled: Boolean, isKeyBackupEnabled: Boolean,
private val matrixRoom: MatrixRoom, private val matrixRoom: MatrixRoom,
private val dispatcher: CoroutineDispatcher, private val dispatcher: CoroutineDispatcher,
private val lastLoginTimestamp: Date?, lastLoginTimestamp: Date?,
private val roomContentForwarder: RoomContentForwarder, private val roomContentForwarder: RoomContentForwarder,
private val onNewSyncedEvent: () -> Unit, private val onNewSyncedEvent: () -> Unit,
) : Timeline { ) : Timeline {
private val initLatch = CompletableDeferred<Unit>() private val initLatch = CompletableDeferred<Unit>()
private val isInit = AtomicBoolean(false) private val isInit = AtomicBoolean(false)
@ -173,7 +172,7 @@ class RustTimeline(
} }
} }
// Use NonCancellable to avoid breaking the timeline when the coroutine is cancelled. // Use NonCancellable to avoid breaking the timeline when the coroutine is cancelled.
override suspend fun paginate(direction: Timeline.PaginationDirection): Result<Boolean> = withContext(NonCancellable) { override suspend fun paginate(direction: Timeline.PaginationDirection): Result<Boolean> = withContext(NonCancellable) {
initLatch.await() initLatch.await()
runCatching { runCatching {
@ -226,7 +225,6 @@ class RustTimeline(
}.let { items -> loadingIndicatorsPostProcessor.process(items, hasMoreToLoadBackward, hasMoreToLoadForward) } }.let { items -> loadingIndicatorsPostProcessor.process(items, hasMoreToLoadBackward, hasMoreToLoadForward) }
// Keep lastForwardIndicatorsPostProcessor last // Keep lastForwardIndicatorsPostProcessor last
.let { items -> lastForwardIndicatorsPostProcessor.process(items) } .let { items -> lastForwardIndicatorsPostProcessor.process(items) }
} }
override fun close() { override fun close() {

View file

@ -25,7 +25,6 @@ import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTime
class LastForwardIndicatorsPostProcessor( class LastForwardIndicatorsPostProcessor(
private val isTimelineLive: Boolean, private val isTimelineLive: Boolean,
) { ) {
private val lastForwardIdentifiers = LinkedHashSet<String>() private val lastForwardIdentifiers = LinkedHashSet<String>()
fun process( fun process(

View file

@ -22,7 +22,6 @@ import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTime
import io.element.android.services.toolbox.api.systemclock.SystemClock import io.element.android.services.toolbox.api.systemclock.SystemClock
class LoadingIndicatorsPostProcessor(private val systemClock: SystemClock) { class LoadingIndicatorsPostProcessor(private val systemClock: SystemClock) {
fun process( fun process(
items: List<MatrixTimelineItem>, items: List<MatrixTimelineItem>,
hasMoreToLoadBackward: Boolean, hasMoreToLoadBackward: Boolean,

View file

@ -29,7 +29,6 @@ import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTime
* or add the RoomBeginning item for non DM room. * or add the RoomBeginning item for non DM room.
*/ */
class RoomBeginningPostProcessor { class RoomBeginningPostProcessor {
fun process( fun process(
items: List<MatrixTimelineItem>, items: List<MatrixTimelineItem>,
isDm: Boolean, isDm: Boolean,
@ -49,7 +48,6 @@ class RoomBeginningPostProcessor {
} }
private fun processForDM(items: List<MatrixTimelineItem>): List<MatrixTimelineItem> { private fun processForDM(items: List<MatrixTimelineItem>): List<MatrixTimelineItem> {
// Find room creation event. This is usually index 0 // Find room creation event. This is usually index 0
val roomCreationEventIndex = items.indexOfFirst { val roomCreationEventIndex = items.indexOfFirst {
val stateEventContent = (it as? MatrixTimelineItem.Event)?.event?.content as? StateContent val stateEventContent = (it as? MatrixTimelineItem.Event)?.event?.content as? StateContent
@ -83,5 +81,4 @@ class RoomBeginningPostProcessor {
virtual = VirtualTimelineItem.RoomBeginning virtual = VirtualTimelineItem.RoomBeginning
) )
} }
} }

View file

@ -32,7 +32,6 @@ import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.api.timeline.ReceiptType
import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler
import io.element.android.tests.testutils.simulateLongTask
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@ -55,14 +54,37 @@ class FakeTimeline(
), ),
override val membershipChangeEventReceived: Flow<Unit> = MutableSharedFlow(), override val membershipChangeEventReceived: Flow<Unit> = MutableSharedFlow(),
) : Timeline { ) : Timeline {
var sendMessageLambda: (
body: String,
htmlBody: String?,
mentions: List<Mention>,
) -> Result<Unit> = { _, _, _ ->
Result.success(Unit)
}
var sendMessageLambda: (body: String, htmlBody: String?, mentions: List<Mention>) -> Result<Unit> = { _, _, _ -> Result.success(Unit) } override suspend fun sendMessage(
override suspend fun sendMessage(body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit> = sendMessageLambda(body, htmlBody, mentions) body: String,
htmlBody: String?,
mentions: List<Mention>,
): Result<Unit> = sendMessageLambda(body, htmlBody, mentions)
var editMessageLambda: (originalEventId: EventId?, transactionId: TransactionId?, body: String, htmlBody: String?, mentions: List<Mention>) -> Result<Unit> = var editMessageLambda: (
{ _, _, _, _, _ -> Result.success(Unit) } originalEventId: EventId?,
transactionId: TransactionId?,
body: String,
htmlBody: String?,
mentions: List<Mention>,
) -> Result<Unit> = { _, _, _, _, _ ->
Result.success(Unit)
}
override suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit> = editMessageLambda( override suspend fun editMessage(
originalEventId: EventId?,
transactionId: TransactionId?,
body: String,
htmlBody: String?,
mentions: List<Mention>,
): Result<Unit> = editMessageLambda(
originalEventId, originalEventId,
transactionId, transactionId,
body, body,
@ -70,23 +92,52 @@ class FakeTimeline(
mentions mentions
) )
var enterSpecialModeLambda: (eventId: EventId?) -> Result<Unit> = { Result.success(Unit) } var enterSpecialModeLambda: (eventId: EventId?) -> Result<Unit> = {
Result.success(Unit)
}
override suspend fun enterSpecialMode(eventId: EventId?): Result<Unit> = enterSpecialModeLambda(eventId) override suspend fun enterSpecialMode(eventId: EventId?): Result<Unit> = enterSpecialModeLambda(eventId)
var replyMessageLambda: (eventId: EventId, body: String, htmlBody: String?, mentions: List<Mention>) -> Result<Unit> = var replyMessageLambda: (
{ _, _, _, _ -> Result.success(Unit) } eventId: EventId,
body: String,
htmlBody: String?,
mentions: List<Mention>,
) -> Result<Unit> = { _, _, _, _ ->
Result.success(Unit)
}
override suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit> = replyMessageLambda( override suspend fun replyMessage(
eventId: EventId,
body: String,
htmlBody: String?,
mentions: List<Mention>,
): Result<Unit> = replyMessageLambda(
eventId, eventId,
body, body,
htmlBody, htmlBody,
mentions mentions
) )
var sendImageLambda: (file: File, thumbnailFile: File?, imageInfo: ImageInfo, body: String?, formattedBody: String?, progressCallback: ProgressCallback?) -> Result<MediaUploadHandler> = var sendImageLambda: (
{ _, _, _, _, _, _ -> Result.success(FakeMediaUploadHandler()) } file: File,
thumbnailFile: File?,
imageInfo: ImageInfo,
body: String?,
formattedBody: String?,
progressCallback: ProgressCallback?,
) -> Result<MediaUploadHandler> = { _, _, _, _, _, _ ->
Result.success(FakeMediaUploadHandler())
}
override suspend fun sendImage(file: File, thumbnailFile: File?, imageInfo: ImageInfo, body: String?, formattedBody: String?, progressCallback: ProgressCallback?): Result<MediaUploadHandler> = sendImageLambda( override suspend fun sendImage(
file: File,
thumbnailFile: File?,
imageInfo: ImageInfo,
body: String?,
formattedBody: String?,
progressCallback: ProgressCallback?,
): Result<MediaUploadHandler> = sendImageLambda(
file, file,
thumbnailFile, thumbnailFile,
imageInfo, imageInfo,
@ -95,10 +146,25 @@ class FakeTimeline(
progressCallback progressCallback
) )
var sendVideoLambda: (file: File, thumbnailFile: File?, videoInfo: VideoInfo, body: String?, formattedBody: String?, progressCallback: ProgressCallback?) -> Result<MediaUploadHandler> = var sendVideoLambda: (
{ _, _, _, _, _, _ -> Result.success(FakeMediaUploadHandler()) } file: File,
thumbnailFile: File?,
videoInfo: VideoInfo,
body: String?,
formattedBody: String?,
progressCallback: ProgressCallback?,
) -> Result<MediaUploadHandler> = { _, _, _, _, _, _ ->
Result.success(FakeMediaUploadHandler())
}
override suspend fun sendVideo(file: File, thumbnailFile: File?, videoInfo: VideoInfo, body: String?, formattedBody: String?, progressCallback: ProgressCallback?): Result<MediaUploadHandler> = sendVideoLambda( override suspend fun sendVideo(
file: File,
thumbnailFile: File?,
videoInfo: VideoInfo,
body: String?,
formattedBody: String?,
progressCallback: ProgressCallback?,
): Result<MediaUploadHandler> = sendVideoLambda(
file, file,
thumbnailFile, thumbnailFile,
videoInfo, videoInfo,
@ -107,19 +173,37 @@ class FakeTimeline(
progressCallback progressCallback
) )
var sendAudioLambda: (file: File, audioInfo: AudioInfo, progressCallback: ProgressCallback?) -> Result<MediaUploadHandler> = var sendAudioLambda: (
{ _, _, _ -> Result.success(FakeMediaUploadHandler()) } file: File,
audioInfo: AudioInfo,
progressCallback: ProgressCallback?,
) -> Result<MediaUploadHandler> = { _, _, _ ->
Result.success(FakeMediaUploadHandler())
}
override suspend fun sendAudio(file: File, audioInfo: AudioInfo, progressCallback: ProgressCallback?): Result<MediaUploadHandler> = sendAudioLambda( override suspend fun sendAudio(
file: File,
audioInfo: AudioInfo,
progressCallback: ProgressCallback?,
): Result<MediaUploadHandler> = sendAudioLambda(
file, file,
audioInfo, audioInfo,
progressCallback progressCallback
) )
var sendFileLambda: (file: File, fileInfo: FileInfo, progressCallback: ProgressCallback?) -> Result<MediaUploadHandler> = var sendFileLambda: (
{ _, _, _ -> Result.success(FakeMediaUploadHandler()) } file: File,
fileInfo: FileInfo,
progressCallback: ProgressCallback?,
) -> Result<MediaUploadHandler> = { _, _, _ ->
Result.success(FakeMediaUploadHandler())
}
override suspend fun sendFile(file: File, fileInfo: FileInfo, progressCallback: ProgressCallback?): Result<MediaUploadHandler> = sendFileLambda( override suspend fun sendFile(
file: File,
fileInfo: FileInfo,
progressCallback: ProgressCallback?,
): Result<MediaUploadHandler> = sendFileLambda(
file, file,
fileInfo, fileInfo,
progressCallback progressCallback
@ -137,10 +221,23 @@ class FakeTimeline(
var cancelSendLambda: (transactionId: TransactionId) -> Result<Unit> = { Result.success(Unit) } var cancelSendLambda: (transactionId: TransactionId) -> Result<Unit> = { Result.success(Unit) }
override suspend fun cancelSend(transactionId: TransactionId): Result<Unit> = cancelSendLambda(transactionId) override suspend fun cancelSend(transactionId: TransactionId): Result<Unit> = cancelSendLambda(transactionId)
var sendLocationLambda: (body: String, geoUri: String, description: String?, zoomLevel: Int?, assetType: AssetType?) -> Result<Unit> = var sendLocationLambda: (
{ _, _, _, _, _ -> Result.success(Unit) } body: String,
geoUri: String,
description: String?,
zoomLevel: Int?,
assetType: AssetType?,
) -> Result<Unit> = { _, _, _, _, _ ->
Result.success(Unit)
}
override suspend fun sendLocation(body: String, geoUri: String, description: String?, zoomLevel: Int?, assetType: AssetType?): Result<Unit> = sendLocationLambda( override suspend fun sendLocation(
body: String,
geoUri: String,
description: String?,
zoomLevel: Int?,
assetType: AssetType?,
): Result<Unit> = sendLocationLambda(
body, body,
geoUri, geoUri,
description, description,
@ -148,20 +245,44 @@ class FakeTimeline(
assetType assetType
) )
var createPollLambda: (question: String, answers: List<String>, maxSelections: Int, pollKind: PollKind) -> Result<Unit> = var createPollLambda: (
{ _, _, _, _ -> Result.success(Unit) } question: String,
answers: List<String>,
maxSelections: Int,
pollKind: PollKind,
) -> Result<Unit> = { _, _, _, _ ->
Result.success(Unit)
}
override suspend fun createPoll(question: String, answers: List<String>, maxSelections: Int, pollKind: PollKind): Result<Unit> = createPollLambda( override suspend fun createPoll(
question: String,
answers: List<String>,
maxSelections: Int,
pollKind: PollKind,
): Result<Unit> = createPollLambda(
question, question,
answers, answers,
maxSelections, maxSelections,
pollKind pollKind
) )
var editPollLambda: (pollStartId: EventId, question: String, answers: List<String>, maxSelections: Int, pollKind: PollKind) -> Result<Unit> = var editPollLambda: (
{ _, _, _, _, _ -> Result.success(Unit) } pollStartId: EventId,
question: String,
answers: List<String>,
maxSelections: Int,
pollKind: PollKind,
) -> Result<Unit> = { _, _, _, _, _ ->
Result.success(Unit)
}
override suspend fun editPoll(pollStartId: EventId, question: String, answers: List<String>, maxSelections: Int, pollKind: PollKind): Result<Unit> = editPollLambda( override suspend fun editPoll(
pollStartId: EventId,
question: String,
answers: List<String>,
maxSelections: Int,
pollKind: PollKind,
): Result<Unit> = editPollLambda(
pollStartId, pollStartId,
question, question,
answers, answers,
@ -169,29 +290,67 @@ class FakeTimeline(
pollKind pollKind
) )
var sendPollResponseLambda: (pollStartId: EventId, answers: List<String>) -> Result<Unit> = { _, _ -> Result.success(Unit) } var sendPollResponseLambda: (
override suspend fun sendPollResponse(pollStartId: EventId, answers: List<String>): Result<Unit> = sendPollResponseLambda(pollStartId, answers) pollStartId: EventId,
answers: List<String>,
) -> Result<Unit> = { _, _ ->
Result.success(Unit)
}
var endPollLambda: (pollStartId: EventId, text: String) -> Result<Unit> = { _, _ -> Result.success(Unit) } override suspend fun sendPollResponse(
override suspend fun endPoll(pollStartId: EventId, text: String): Result<Unit> = endPollLambda(pollStartId, text) pollStartId: EventId,
answers: List<String>,
): Result<Unit> = sendPollResponseLambda(pollStartId, answers)
var sendVoiceMessageLambda: (file: File, audioInfo: AudioInfo, waveform: List<Float>, progressCallback: ProgressCallback?) -> Result<MediaUploadHandler> = var endPollLambda: (
{ _, _, _, _ -> Result.success(FakeMediaUploadHandler()) } pollStartId: EventId,
text: String,
) -> Result<Unit> = { _, _ ->
Result.success(Unit)
}
override suspend fun sendVoiceMessage(file: File, audioInfo: AudioInfo, waveform: List<Float>, progressCallback: ProgressCallback?): Result<MediaUploadHandler> = sendVoiceMessageLambda( override suspend fun endPoll(
pollStartId: EventId,
text: String,
): Result<Unit> = endPollLambda(pollStartId, text)
var sendVoiceMessageLambda: (
file: File,
audioInfo: AudioInfo,
waveform: List<Float>,
progressCallback: ProgressCallback?,
) -> Result<MediaUploadHandler> = { _, _, _, _ ->
Result.success(FakeMediaUploadHandler())
}
override suspend fun sendVoiceMessage(
file: File,
audioInfo: AudioInfo,
waveform: List<Float>,
progressCallback: ProgressCallback?,
): Result<MediaUploadHandler> = sendVoiceMessageLambda(
file, file,
audioInfo, audioInfo,
waveform, waveform,
progressCallback progressCallback
) )
var sendReadReceiptLambda: (eventId: EventId, receiptType: ReceiptType) -> Result<Unit> = { _, _ -> Result.success(Unit) } var sendReadReceiptLambda: (
eventId: EventId,
receiptType: ReceiptType,
) -> Result<Unit> = { _, _ ->
Result.success(Unit)
}
override suspend fun sendReadReceipt( override suspend fun sendReadReceipt(
eventId: EventId, eventId: EventId,
receiptType: ReceiptType, receiptType: ReceiptType,
): Result<Unit> = sendReadReceiptLambda(eventId, receiptType) ): Result<Unit> = sendReadReceiptLambda(eventId, receiptType)
var paginateLambda: (direction: Timeline.PaginationDirection) -> Result<Boolean> = { Result.success(false) } var paginateLambda: (direction: Timeline.PaginationDirection) -> Result<Boolean> = {
Result.success(false)
}
override suspend fun paginate(direction: Timeline.PaginationDirection): Result<Boolean> = paginateLambda(direction) override suspend fun paginate(direction: Timeline.PaginationDirection): Result<Boolean> = paginateLambda(direction)
override fun paginationStatus(direction: Timeline.PaginationDirection): StateFlow<Timeline.PaginationStatus> { override fun paginationStatus(direction: Timeline.PaginationDirection): StateFlow<Timeline.PaginationStatus> {