forked from GitHub-Mirror/riotX-android
Timeline : introduce timeline data class to allow listening for isLoadingForward and isLoadingBackward
This commit is contained in:
@ -3,9 +3,10 @@ package im.vector.matrix.android.api.session.room
|
||||
import android.arch.lifecycle.LiveData
|
||||
import im.vector.matrix.android.api.session.room.model.MyMembership
|
||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineService
|
||||
import im.vector.matrix.android.api.util.Cancelable
|
||||
|
||||
interface Room : TimelineHolder, SendService {
|
||||
interface Room : TimelineService, SendService {
|
||||
|
||||
val roomId: String
|
||||
|
||||
|
@ -1,11 +0,0 @@
|
||||
package im.vector.matrix.android.api.session.room
|
||||
|
||||
import android.arch.lifecycle.LiveData
|
||||
import android.arch.paging.PagedList
|
||||
import im.vector.matrix.android.api.session.events.model.EnrichedEvent
|
||||
|
||||
interface TimelineHolder {
|
||||
|
||||
fun timeline(eventId: String? = null): LiveData<PagedList<EnrichedEvent>>
|
||||
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package im.vector.matrix.android.api.session.room.timeline
|
||||
|
||||
import android.arch.paging.PagedList
|
||||
import im.vector.matrix.android.api.session.events.model.EnrichedEvent
|
||||
|
||||
data class TimelineData(
|
||||
val events: PagedList<EnrichedEvent>,
|
||||
val isLoadingForward: Boolean = false,
|
||||
val isLoadingBackward: Boolean = false
|
||||
)
|
@ -0,0 +1,9 @@
|
||||
package im.vector.matrix.android.api.session.room.timeline
|
||||
|
||||
import android.arch.lifecycle.LiveData
|
||||
|
||||
interface TimelineService {
|
||||
|
||||
fun timeline(eventId: String? = null): LiveData<TimelineData>
|
||||
|
||||
}
|
@ -9,10 +9,11 @@ import im.vector.matrix.android.api.session.events.model.EnrichedEvent
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.api.session.room.Room
|
||||
import im.vector.matrix.android.api.session.room.SendService
|
||||
import im.vector.matrix.android.api.session.room.TimelineHolder
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineService
|
||||
import im.vector.matrix.android.api.session.room.model.Membership
|
||||
import im.vector.matrix.android.api.session.room.model.MyMembership
|
||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineData
|
||||
import im.vector.matrix.android.api.util.Cancelable
|
||||
import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
||||
@ -32,7 +33,7 @@ internal data class DefaultRoom(
|
||||
|
||||
private val loadRoomMembersTask by inject<LoadRoomMembersTask>()
|
||||
private val monarchy by inject<Monarchy>()
|
||||
private val timelineHolder by inject<TimelineHolder> { parametersOf(roomId) }
|
||||
private val timelineService by inject<TimelineService> { parametersOf(roomId) }
|
||||
private val sendService by inject<SendService> { parametersOf(roomId) }
|
||||
private val taskExecutor by inject<TaskExecutor>()
|
||||
|
||||
@ -47,8 +48,8 @@ internal data class DefaultRoom(
|
||||
}
|
||||
}
|
||||
|
||||
override fun timeline(eventId: String?): LiveData<PagedList<EnrichedEvent>> {
|
||||
return timelineHolder.timeline(eventId)
|
||||
override fun timeline(eventId: String?): LiveData<TimelineData> {
|
||||
return timelineService.timeline(eventId)
|
||||
}
|
||||
|
||||
override fun loadRoomMembersIfNeeded(): Cancelable {
|
||||
|
@ -2,14 +2,20 @@ package im.vector.matrix.android.internal.session.room
|
||||
|
||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
||||
import im.vector.matrix.android.api.session.room.SendService
|
||||
import im.vector.matrix.android.api.session.room.TimelineHolder
|
||||
import im.vector.matrix.android.api.session.room.send.EventFactory
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineService
|
||||
import im.vector.matrix.android.internal.session.DefaultSession
|
||||
import im.vector.matrix.android.internal.session.room.members.DefaultLoadRoomMembersTask
|
||||
import im.vector.matrix.android.internal.session.room.members.LoadRoomMembersTask
|
||||
import im.vector.matrix.android.internal.session.room.members.RoomMemberExtractor
|
||||
import im.vector.matrix.android.internal.session.room.send.DefaultSendService
|
||||
import im.vector.matrix.android.internal.session.room.timeline.*
|
||||
import im.vector.matrix.android.internal.session.room.timeline.DefaultGetContextOfEventTask
|
||||
import im.vector.matrix.android.internal.session.room.timeline.DefaultPaginationTask
|
||||
import im.vector.matrix.android.internal.session.room.timeline.DefaultTimelineService
|
||||
import im.vector.matrix.android.internal.session.room.timeline.GetContextOfEventTask
|
||||
import im.vector.matrix.android.internal.session.room.timeline.PaginationTask
|
||||
import im.vector.matrix.android.internal.session.room.timeline.TimelineBoundaryCallback
|
||||
import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEventPersistor
|
||||
import im.vector.matrix.android.internal.util.PagingRequestHelper
|
||||
import org.koin.dsl.module.module
|
||||
import retrofit2.Retrofit
|
||||
@ -50,7 +56,7 @@ class RoomModule {
|
||||
val helper = PagingRequestHelper(Executors.newSingleThreadExecutor())
|
||||
val timelineBoundaryCallback = TimelineBoundaryCallback(roomId, get(), get(), get(), helper)
|
||||
val roomMemberExtractor = RoomMemberExtractor(get(), roomId)
|
||||
DefaultTimelineHolder(roomId, get(), get(), timelineBoundaryCallback, get(), roomMemberExtractor) as TimelineHolder
|
||||
DefaultTimelineService(roomId, get(), get(), timelineBoundaryCallback, get(), roomMemberExtractor) as TimelineService
|
||||
}
|
||||
|
||||
factory { (roomId: String) ->
|
||||
|
@ -1,10 +1,9 @@
|
||||
package im.vector.matrix.android.internal.session.room.timeline
|
||||
|
||||
import arrow.core.Try
|
||||
import arrow.core.failure
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.session.room.RoomAPI
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import im.vector.matrix.android.internal.util.FilterUtil
|
||||
|
||||
|
||||
@ -12,7 +11,7 @@ internal interface PaginationTask : Task<PaginationTask.Params, TokenChunkEvent>
|
||||
|
||||
data class Params(
|
||||
val roomId: String,
|
||||
val from: String?,
|
||||
val from: String,
|
||||
val direction: PaginationDirection,
|
||||
val limit: Int
|
||||
)
|
||||
@ -24,9 +23,6 @@ internal class DefaultPaginationTask(private val roomAPI: RoomAPI,
|
||||
) : PaginationTask {
|
||||
|
||||
override fun execute(params: PaginationTask.Params): Try<TokenChunkEvent> {
|
||||
if (params.from == null) {
|
||||
return RuntimeException("From token shouldn't be null").failure()
|
||||
}
|
||||
val filter = FilterUtil.createRoomEventFilter(true)?.toJSONString()
|
||||
return executeRequest<PaginationResponse> {
|
||||
apiCall = roomAPI.getRoomMessagesFrom(params.roomId, params.from, params.direction.value, params.limit, filter)
|
||||
@ -36,4 +32,5 @@ internal class DefaultPaginationTask(private val roomAPI: RoomAPI,
|
||||
.map { chunk }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -6,7 +6,8 @@ import android.arch.paging.PagedList
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.api.session.events.interceptor.EnrichedEventInterceptor
|
||||
import im.vector.matrix.android.api.session.events.model.EnrichedEvent
|
||||
import im.vector.matrix.android.api.session.room.TimelineHolder
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineData
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineService
|
||||
import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||
import im.vector.matrix.android.internal.database.model.ChunkEntityFields
|
||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||
@ -15,23 +16,25 @@ import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.session.room.members.RoomMemberExtractor
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.task.configureWith
|
||||
import im.vector.matrix.android.internal.util.LiveDataUtils
|
||||
import im.vector.matrix.android.internal.util.PagingRequestHelper
|
||||
import im.vector.matrix.android.internal.util.tryTransactionAsync
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmQuery
|
||||
|
||||
private const val PAGE_SIZE = 30
|
||||
|
||||
internal class DefaultTimelineHolder(private val roomId: String,
|
||||
private val monarchy: Monarchy,
|
||||
private val taskExecutor: TaskExecutor,
|
||||
private val boundaryCallback: TimelineBoundaryCallback,
|
||||
private val contextOfEventTask: GetContextOfEventTask,
|
||||
private val roomMemberExtractor: RoomMemberExtractor
|
||||
) : TimelineHolder {
|
||||
internal class DefaultTimelineService(private val roomId: String,
|
||||
private val monarchy: Monarchy,
|
||||
private val taskExecutor: TaskExecutor,
|
||||
private val boundaryCallback: TimelineBoundaryCallback,
|
||||
private val contextOfEventTask: GetContextOfEventTask,
|
||||
private val roomMemberExtractor: RoomMemberExtractor
|
||||
) : TimelineService {
|
||||
|
||||
private val eventInterceptors = ArrayList<EnrichedEventInterceptor>()
|
||||
|
||||
override fun timeline(eventId: String?): LiveData<PagedList<EnrichedEvent>> {
|
||||
override fun timeline(eventId: String?): LiveData<TimelineData> {
|
||||
clearUnlinkedEvents()
|
||||
if (eventId != null) {
|
||||
fetchEventIfNeeded(eventId)
|
||||
@ -51,9 +54,16 @@ internal class DefaultTimelineHolder(private val roomId: String,
|
||||
.build()
|
||||
|
||||
val livePagedListBuilder = LivePagedListBuilder(domainSourceFactory, pagedListConfig).setBoundaryCallback(boundaryCallback)
|
||||
return monarchy.findAllPagedWithChanges(realmDataSourceFactory, livePagedListBuilder)
|
||||
val eventsLiveData = monarchy.findAllPagedWithChanges(realmDataSourceFactory, livePagedListBuilder)
|
||||
|
||||
return LiveDataUtils.combine(eventsLiveData, boundaryCallback.status) { events, status ->
|
||||
val isLoadingForward = status.before == PagingRequestHelper.Status.RUNNING
|
||||
val isLoadingBackward = status.after == PagingRequestHelper.Status.RUNNING
|
||||
TimelineData(events, isLoadingForward, isLoadingBackward)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun clearUnlinkedEvents() {
|
||||
monarchy.tryTransactionAsync { realm ->
|
||||
val unlinkedEvents = EventEntity
|
@ -1,5 +1,6 @@
|
||||
package im.vector.matrix.android.internal.session.room.timeline
|
||||
|
||||
import android.arch.lifecycle.LiveData
|
||||
import android.arch.paging.PagedList
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
@ -20,35 +21,60 @@ internal class TimelineBoundaryCallback(private val roomId: String,
|
||||
|
||||
var limit = 30
|
||||
|
||||
val status = object : LiveData<PagingRequestHelper.StatusReport>() {
|
||||
|
||||
init {
|
||||
value = PagingRequestHelper.StatusReport.createDefault()
|
||||
}
|
||||
|
||||
val listener = PagingRequestHelper.Listener { postValue(it) }
|
||||
|
||||
override fun onActive() {
|
||||
helper.addListener(listener)
|
||||
}
|
||||
|
||||
override fun onInactive() {
|
||||
helper.removeListener(listener)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onZeroItemsLoaded() {
|
||||
// actually, it's not possible
|
||||
}
|
||||
|
||||
override fun onItemAtEndLoaded(itemAtEnd: EnrichedEvent) {
|
||||
val token = itemAtEnd.root.eventId?.let { getToken(it, PaginationDirection.BACKWARDS) }
|
||||
?: return
|
||||
|
||||
helper.runIfNotRunning(PagingRequestHelper.RequestType.AFTER) {
|
||||
runPaginationRequest(it, itemAtEnd, PaginationDirection.BACKWARDS)
|
||||
runPaginationRequest(it, token, PaginationDirection.BACKWARDS)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onItemAtFrontLoaded(itemAtFront: EnrichedEvent) {
|
||||
val token = itemAtFront.root.eventId?.let { getToken(it, PaginationDirection.FORWARDS) }
|
||||
?: return
|
||||
|
||||
helper.runIfNotRunning(PagingRequestHelper.RequestType.BEFORE) {
|
||||
runPaginationRequest(it, itemAtFront, PaginationDirection.FORWARDS)
|
||||
runPaginationRequest(it, token, PaginationDirection.FORWARDS)
|
||||
}
|
||||
}
|
||||
|
||||
private fun runPaginationRequest(requestCallback: PagingRequestHelper.Request.Callback,
|
||||
item: EnrichedEvent,
|
||||
direction: PaginationDirection) {
|
||||
private fun getToken(eventId: String, direction: PaginationDirection): String? {
|
||||
var token: String? = null
|
||||
monarchy.doWithRealm { realm ->
|
||||
if (item.root.eventId == null) {
|
||||
return@doWithRealm
|
||||
}
|
||||
val chunkEntity = ChunkEntity.findAllIncludingEvents(realm, Collections.singletonList(item.root.eventId)).firstOrNull()
|
||||
val chunkEntity = ChunkEntity.findAllIncludingEvents(realm, Collections.singletonList(eventId)).firstOrNull()
|
||||
token = if (direction == PaginationDirection.FORWARDS) chunkEntity?.nextToken else chunkEntity?.prevToken
|
||||
}
|
||||
return token
|
||||
}
|
||||
|
||||
private fun runPaginationRequest(requestCallback: PagingRequestHelper.Request.Callback,
|
||||
from: String,
|
||||
direction: PaginationDirection) {
|
||||
|
||||
val params = PaginationTask.Params(roomId = roomId,
|
||||
from = token,
|
||||
from = from,
|
||||
direction = direction,
|
||||
limit = limit)
|
||||
|
||||
|
@ -0,0 +1,38 @@
|
||||
package im.vector.matrix.android.internal.util
|
||||
|
||||
import android.arch.lifecycle.LiveData
|
||||
import android.arch.lifecycle.MediatorLiveData
|
||||
|
||||
object LiveDataUtils {
|
||||
|
||||
fun <FIRST, SECOND, OUT> combine(firstSource: LiveData<FIRST>,
|
||||
secondSource: LiveData<SECOND>,
|
||||
mapper: (FIRST, SECOND) -> OUT): LiveData<OUT> {
|
||||
|
||||
return MediatorLiveData<OUT>().apply {
|
||||
var firstValue: FIRST? = null
|
||||
var secondValue: SECOND? = null
|
||||
|
||||
val valueDispatcher = {
|
||||
firstValue?.let { safeFirst ->
|
||||
secondValue?.let { safeSecond ->
|
||||
val mappedValue = mapper(safeFirst, safeSecond)
|
||||
postValue(mappedValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
addSource(firstSource) {
|
||||
firstValue = it
|
||||
valueDispatcher()
|
||||
}
|
||||
|
||||
addSource(secondSource) {
|
||||
secondValue = it
|
||||
valueDispatcher()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -379,6 +379,11 @@ public class PagingRequestHelper {
|
||||
@NonNull
|
||||
private final Throwable[] mErrors;
|
||||
|
||||
public static StatusReport createDefault() {
|
||||
final Throwable[] errors = {};
|
||||
return new StatusReport(Status.SUCCESS, Status.SUCCESS, Status.SUCCESS, errors);
|
||||
}
|
||||
|
||||
StatusReport(@NonNull Status initial, @NonNull Status before, @NonNull Status after,
|
||||
@NonNull Throwable[] errors) {
|
||||
this.initial = initial;
|
||||
|
Reference in New Issue
Block a user