Browse Source

Timeline: use val instead of fun for flows

jonny/proxy
ganfra 1 year ago
parent
commit
3aa5cce8dc
  1. 16
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt
  2. 11
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt
  3. 4
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/MatrixTimeline.kt
  4. 17
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt
  5. 17
      libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeMatrixTimeline.kt
  6. 6
      samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt

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

@ -59,13 +59,8 @@ class TimelinePresenter @Inject constructor(
var lastReadMarkerIndex by rememberSaveable { mutableStateOf(Int.MAX_VALUE) } var lastReadMarkerIndex by rememberSaveable { mutableStateOf(Int.MAX_VALUE) }
var lastReadMarkerId by rememberSaveable { mutableStateOf<EventId?>(null) } var lastReadMarkerId by rememberSaveable { mutableStateOf<EventId?>(null) }
val timelineItems = timelineItemsFactory val timelineItems = timelineItemsFactory.collectItemsAsState()
.flow() val paginationState = timeline.paginationState.collectAsState()
.collectAsState()
val paginationState = timeline
.paginationState()
.collectAsState()
fun handleEvents(event: TimelineEvents) { fun handleEvents(event: TimelineEvents) {
when (event) { when (event) {
@ -85,13 +80,8 @@ class TimelinePresenter @Inject constructor(
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
timeline timeline
.timelineItems() .timelineItems
.onEach(timelineItemsFactory::replaceWith) .onEach(timelineItemsFactory::replaceWith)
.onEach { timelineItems ->
if (timelineItems.isEmpty()) {
loadMore(paginationState.value)
}
}
.launchIn(this) .launchIn(this)
} }

11
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt

@ -16,6 +16,9 @@
package io.element.android.features.messages.impl.timeline.factories package io.element.android.features.messages.impl.timeline.factories
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import io.element.android.features.messages.impl.timeline.diff.CacheInvalidator import io.element.android.features.messages.impl.timeline.diff.CacheInvalidator
import io.element.android.features.messages.impl.timeline.diff.MatrixTimelineItemsDiffCallback import io.element.android.features.messages.impl.timeline.diff.MatrixTimelineItemsDiffCallback
@ -27,11 +30,8 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
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.flow.asStateFlow
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
@ -55,7 +55,10 @@ class TimelineItemsFactory @Inject constructor(
private val lock = Mutex() private val lock = Mutex()
private val cacheInvalidator = CacheInvalidator(timelineItemsCache) private val cacheInvalidator = CacheInvalidator(timelineItemsCache)
fun flow(): StateFlow<ImmutableList<TimelineItem>> = timelineItems.asStateFlow() @Composable
fun collectItemsAsState(): State<ImmutableList<TimelineItem>> {
return timelineItems.collectAsState()
}
suspend fun replaceWith( suspend fun replaceWith(
timelineItems: List<MatrixTimelineItem>, timelineItems: List<MatrixTimelineItem>,

4
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/MatrixTimeline.kt

@ -27,8 +27,8 @@ interface MatrixTimeline {
val canBackPaginate: Boolean val canBackPaginate: Boolean
) )
fun paginationState(): StateFlow<PaginationState> val paginationState: StateFlow<PaginationState>
fun timelineItems(): Flow<List<MatrixTimelineItem>> val timelineItems: Flow<List<MatrixTimelineItem>>
suspend fun paginateBackwards(requestSize: Int, untilNumberOfItems: Int): Result<Unit> suspend fun paginateBackwards(requestSize: Int, untilNumberOfItems: Int): Result<Unit>
suspend fun fetchDetailsForEvent(eventId: EventId): Result<Unit> suspend fun fetchDetailsForEvent(eventId: EventId): Result<Unit>

17
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt

@ -30,6 +30,7 @@ import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.Flow 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.asStateFlow
import kotlinx.coroutines.flow.sample import kotlinx.coroutines.flow.sample
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.matrix.rustcomponents.sdk.PaginationOptions import org.matrix.rustcomponents.sdk.PaginationOptions
@ -45,10 +46,10 @@ class RustMatrixTimeline(
private val coroutineDispatchers: CoroutineDispatchers, private val coroutineDispatchers: CoroutineDispatchers,
) : MatrixTimeline { ) : MatrixTimeline {
private val timelineItems: MutableStateFlow<List<MatrixTimelineItem>> = private val _timelineItems: MutableStateFlow<List<MatrixTimelineItem>> =
MutableStateFlow(emptyList()) MutableStateFlow(emptyList())
private val paginationState = MutableStateFlow( private val _paginationState = MutableStateFlow(
MatrixTimeline.PaginationState(canBackPaginate = true, isBackPaginating = false) MatrixTimeline.PaginationState(canBackPaginate = true, isBackPaginating = false)
) )
@ -64,19 +65,15 @@ class RustMatrixTimeline(
) )
private val timelineDiffProcessor = MatrixTimelineDiffProcessor( private val timelineDiffProcessor = MatrixTimelineDiffProcessor(
paginationState = paginationState, paginationState = _paginationState,
timelineItems = timelineItems, timelineItems = _timelineItems,
timelineItemFactory = timelineItemFactory, timelineItemFactory = timelineItemFactory,
) )
override fun paginationState(): StateFlow<MatrixTimeline.PaginationState> { override val paginationState: StateFlow<MatrixTimeline.PaginationState> = _paginationState.asStateFlow()
return paginationState
}
@OptIn(FlowPreview::class) @OptIn(FlowPreview::class)
override fun timelineItems(): Flow<List<MatrixTimelineItem>> { override val timelineItems: Flow<List<MatrixTimelineItem>> = _timelineItems.sample(50)
return timelineItems.sample(50)
}
internal suspend fun postItems(items: List<TimelineItem>) { internal suspend fun postItems(items: List<TimelineItem>) {
timelineDiffProcessor.postItems(items) timelineDiffProcessor.postItems(items)

17
libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeMatrixTimeline.kt

@ -23,33 +23,30 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow 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.getAndUpdate
class FakeMatrixTimeline( class FakeMatrixTimeline(
initialTimelineItems: List<MatrixTimelineItem> = emptyList(), initialTimelineItems: List<MatrixTimelineItem> = emptyList(),
initialPaginationState: MatrixTimeline.PaginationState = MatrixTimeline.PaginationState(canBackPaginate = true, isBackPaginating = false) initialPaginationState: MatrixTimeline.PaginationState = MatrixTimeline.PaginationState(canBackPaginate = true, isBackPaginating = false)
) : MatrixTimeline { ) : MatrixTimeline {
private val paginationState: MutableStateFlow<MatrixTimeline.PaginationState> = MutableStateFlow(initialPaginationState) private val _paginationState: MutableStateFlow<MatrixTimeline.PaginationState> = MutableStateFlow(initialPaginationState)
private val timelineItems: MutableStateFlow<List<MatrixTimelineItem>> = MutableStateFlow(initialTimelineItems) private val _timelineItems: MutableStateFlow<List<MatrixTimelineItem>> = MutableStateFlow(initialTimelineItems)
var sendReadReceiptCount = 0 var sendReadReceiptCount = 0
private set private set
fun updatePaginationState(update: (MatrixTimeline.PaginationState.() -> MatrixTimeline.PaginationState)) { fun updatePaginationState(update: (MatrixTimeline.PaginationState.() -> MatrixTimeline.PaginationState)) {
paginationState.value = update(paginationState.value) _paginationState.getAndUpdate(update)
} }
fun updateTimelineItems(update: (items: List<MatrixTimelineItem>) -> List<MatrixTimelineItem>) { fun updateTimelineItems(update: (items: List<MatrixTimelineItem>) -> List<MatrixTimelineItem>) {
timelineItems.value = update(timelineItems.value) _timelineItems.getAndUpdate(update)
} }
override fun paginationState(): StateFlow<MatrixTimeline.PaginationState> { override val paginationState: StateFlow<MatrixTimeline.PaginationState> = _paginationState
return paginationState
}
override fun timelineItems(): Flow<List<MatrixTimelineItem>> { override val timelineItems: Flow<List<MatrixTimelineItem>> = _timelineItems
return timelineItems
}
override suspend fun paginateBackwards(requestSize: Int, untilNumberOfItems: Int): Result<Unit> { override suspend fun paginateBackwards(requestSize: Int, untilNumberOfItems: Int): Result<Unit> {
updatePaginationState { updatePaginationState {

6
samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt

@ -82,11 +82,7 @@ class RoomListScreen(
withContext(coroutineDispatchers.io) { withContext(coroutineDispatchers.io) {
matrixClient.getRoom(roomId)!!.use { room -> matrixClient.getRoom(roomId)!!.use { room ->
room.open() room.open()
val timeline = room.timeline room.timeline.paginateBackwards(20, 50)
timeline.apply {
// TODO This doesn't work reliably as initialize is asynchronous, and the timeline can't be used until it's finished
paginateBackwards(20, 50)
}
} }
} }
} }

Loading…
Cancel
Save