Media Gallery

This commit is contained in:
Benoit Marty 2024-12-09 16:45:46 +01:00
parent 61805646c8
commit 28725a364f
69 changed files with 3822 additions and 56 deletions

View file

@ -107,6 +107,11 @@ interface MatrixRoom : Closeable {
*/
suspend fun pinnedEventsTimeline(): Result<Timeline>
/**
* Create a new timeline for the media events of the room.
*/
suspend fun mediaTimeline(): Result<Timeline>
fun destroy()
suspend fun subscribeToSync()

View file

@ -42,7 +42,8 @@ interface Timeline : AutoCloseable {
enum class Mode {
LIVE,
FOCUSED_ON_EVENT,
PINNED_EVENTS
PINNED_EVENTS,
MEDIA,
}
val membershipChangeEventReceived: Flow<Unit>

View file

@ -78,6 +78,7 @@ import org.matrix.rustcomponents.sdk.IdentityStatusChangeListener
import org.matrix.rustcomponents.sdk.RoomInfo
import org.matrix.rustcomponents.sdk.RoomInfoListener
import org.matrix.rustcomponents.sdk.RoomListItem
import org.matrix.rustcomponents.sdk.RoomMessageEventMessageType
import org.matrix.rustcomponents.sdk.TypingNotificationsListener
import org.matrix.rustcomponents.sdk.UserPowerLevelUpdate
import org.matrix.rustcomponents.sdk.WidgetCapabilities
@ -223,6 +224,26 @@ class RustMatrixRoom(
}
}
override suspend fun mediaTimeline(): Result<Timeline> {
return runCatching {
innerRoom.messageFilteredTimeline(
internalIdPrefix = "MediaGallery_",
allowedMessageTypes = listOf(
RoomMessageEventMessageType.FILE,
RoomMessageEventMessageType.IMAGE,
RoomMessageEventMessageType.VIDEO,
RoomMessageEventMessageType.AUDIO,
)
).let { inner ->
createTimeline(inner, mode = Timeline.Mode.MEDIA)
}
}.onFailure {
if (it is CancellationException) {
throw it
}
}
}
override fun destroy() {
roomCoroutineScope.cancel()
liveTimeline.close()

View file

@ -64,6 +64,8 @@ import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import org.matrix.rustcomponents.sdk.EditedContent
import org.matrix.rustcomponents.sdk.FormattedBody
@ -170,26 +172,36 @@ class RustTimeline(
}
}
private val backwardsPaginationMutex = Mutex()
private val forwardsPaginationMutex = Mutex()
private fun getPaginationMutex(direction: Timeline.PaginationDirection) = when (direction) {
Timeline.PaginationDirection.BACKWARDS -> backwardsPaginationMutex
Timeline.PaginationDirection.FORWARDS -> forwardsPaginationMutex
}
// Use NonCancellable to avoid breaking the timeline when the coroutine is cancelled.
override suspend fun paginate(direction: Timeline.PaginationDirection): Result<Boolean> = withContext(NonCancellable) {
withContext(dispatcher) {
initLatch.await()
runCatching {
if (!canPaginate(direction)) throw TimelineException.CannotPaginate
updatePaginationStatus(direction) { it.copy(isPaginating = true) }
when (direction) {
Timeline.PaginationDirection.BACKWARDS -> inner.paginateBackwards(PAGINATION_SIZE.toUShort())
Timeline.PaginationDirection.FORWARDS -> inner.focusedPaginateForwards(PAGINATION_SIZE.toUShort())
getPaginationMutex(direction).withLock {
runCatching {
if (!canPaginate(direction)) throw TimelineException.CannotPaginate
updatePaginationStatus(direction) { it.copy(isPaginating = true) }
when (direction) {
Timeline.PaginationDirection.BACKWARDS -> inner.paginateBackwards(PAGINATION_SIZE.toUShort())
Timeline.PaginationDirection.FORWARDS -> inner.focusedPaginateForwards(PAGINATION_SIZE.toUShort())
}
}.onFailure { error ->
updatePaginationStatus(direction) { it.copy(isPaginating = false) }
if (error is TimelineException.CannotPaginate) {
Timber.d("Can't paginate $direction on room ${matrixRoom.roomId} with paginationStatus: ${backPaginationStatus.value}")
} else {
Timber.e(error, "Error paginating $direction on room ${matrixRoom.roomId}")
}
}.onSuccess { hasReachedEnd ->
updatePaginationStatus(direction) { it.copy(isPaginating = false, hasMoreToLoad = !hasReachedEnd) }
}
}.onFailure { error ->
updatePaginationStatus(direction) { it.copy(isPaginating = false) }
if (error is TimelineException.CannotPaginate) {
Timber.d("Can't paginate $direction on room ${matrixRoom.roomId} with paginationStatus: ${backPaginationStatus.value}")
} else {
Timber.e(error, "Error paginating $direction on room ${matrixRoom.roomId}")
}
}.onSuccess { hasReachedEnd ->
updatePaginationStatus(direction) { it.copy(isPaginating = false, hasMoreToLoad = !hasReachedEnd) }
}
}
}

View file

@ -133,6 +133,7 @@ class FakeMatrixRoom(
private val getMembersResult: (Int) -> Result<List<RoomMember>> = { lambdaError() },
private val timelineFocusedOnEventResult: (EventId) -> Result<Timeline> = { lambdaError() },
private val pinnedEventsTimelineResult: () -> Result<Timeline> = { lambdaError() },
private val mediaTimelineResult: () -> Result<Timeline> = { lambdaError() },
private val setSendQueueEnabledLambda: (Boolean) -> Unit = { _: Boolean -> },
private val saveComposerDraftLambda: (ComposerDraft) -> Result<Unit> = { _: ComposerDraft -> Result.success(Unit) },
private val loadComposerDraftLambda: () -> Result<ComposerDraft?> = { Result.success<ComposerDraft?>(null) },
@ -203,6 +204,10 @@ class FakeMatrixRoom(
pinnedEventsTimelineResult()
}
override suspend fun mediaTimeline(): Result<Timeline> = simulateLongTask {
mediaTimelineResult()
}
override suspend fun subscribeToSync() {
subscribeToSyncLambda()
}