forked from GitHub-Mirror/riotX-android
Makes back pagination work. Still need to refine but ok for proto.
This commit is contained in:
parent
d71ae02162
commit
0f4d15e488
BIN
.idea/caches/build_file_checksums.ser
generated
BIN
.idea/caches/build_file_checksums.ser
generated
Binary file not shown.
@ -17,7 +17,7 @@ import im.vector.riotredesign.core.utils.FragmentArgumentDelegate
|
|||||||
import kotlinx.android.synthetic.main.fragment_room_detail.*
|
import kotlinx.android.synthetic.main.fragment_room_detail.*
|
||||||
import org.koin.android.ext.android.inject
|
import org.koin.android.ext.android.inject
|
||||||
|
|
||||||
class RoomDetailFragment : RiotFragment(), TimelineAdapter.Callback {
|
class RoomDetailFragment : RiotFragment(), TimelineEventAdapter.Callback {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
@ -31,7 +31,8 @@ class RoomDetailFragment : RiotFragment(), TimelineAdapter.Callback {
|
|||||||
private val matrix by inject<Matrix>()
|
private val matrix by inject<Matrix>()
|
||||||
private val currentSession = matrix.currentSession!!
|
private val currentSession = matrix.currentSession!!
|
||||||
private var roomId by FragmentArgumentDelegate<String>()
|
private var roomId by FragmentArgumentDelegate<String>()
|
||||||
private val adapter = TimelineAdapter(this)
|
private val timelineAdapter = TimelineEventAdapter(this)
|
||||||
|
private val timelineEventController = TimelineEventController()
|
||||||
private lateinit var room: Room
|
private lateinit var room: Room
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
@ -46,26 +47,27 @@ class RoomDetailFragment : RiotFragment(), TimelineAdapter.Callback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun renderEvents(events: PagedList<Event>?) {
|
private fun renderEvents(events: PagedList<Event>?) {
|
||||||
adapter.submitList(events)
|
timelineAdapter.submitList(events)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupRecyclerView() {
|
private fun setupRecyclerView() {
|
||||||
val layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
|
val layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, true)
|
||||||
adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
layoutManager.stackFromEnd = true
|
||||||
|
timelineAdapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
||||||
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
|
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
|
||||||
if (layoutManager.findLastCompletelyVisibleItemPosition() == positionStart - itemCount) {
|
if (layoutManager.findFirstVisibleItemPosition() == 0) {
|
||||||
layoutManager.scrollToPosition(adapter.itemCount - 1)
|
layoutManager.scrollToPosition(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
recyclerView.layoutManager = layoutManager
|
recyclerView.layoutManager = layoutManager
|
||||||
recyclerView.adapter = adapter
|
recyclerView.adapter = timelineAdapter
|
||||||
recyclerView.setHasFixedSize(true)
|
//recyclerView.setController(timelineEventController)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onEventsListChanged(oldList: List<Event>?, newList: List<Event>?) {
|
override fun onEventsListChanged(oldList: List<Event>?, newList: List<Event>?) {
|
||||||
if (oldList == null && newList != null) {
|
if (oldList == null && newList != null) {
|
||||||
recyclerView.scrollToPosition(newList.size - 1)
|
recyclerView.scrollToPosition(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,8 +14,8 @@ import im.vector.riotredesign.R
|
|||||||
* Created by francois on 14/05/2018.
|
* Created by francois on 14/05/2018.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class TimelineAdapter(private val callback: Callback? = null)
|
class TimelineEventAdapter(private val callback: Callback? = null)
|
||||||
: PagedListAdapter<Event, TimelineAdapter.ViewHolder>(EventDiffUtilCallback()) {
|
: PagedListAdapter<Event, TimelineEventAdapter.ViewHolder>(EventDiffUtilCallback()) {
|
||||||
|
|
||||||
|
|
||||||
private var currentList: List<Event>? = null
|
private var currentList: List<Event>? = null
|
@ -3,7 +3,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<android.support.v7.widget.RecyclerView
|
<com.airbnb.epoxy.EpoxyRecyclerView
|
||||||
android:id="@+id/recyclerView"
|
android:id="@+id/recyclerView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="match_parent" />
|
||||||
|
@ -7,7 +7,6 @@ import io.realm.annotations.LinkingObjects
|
|||||||
|
|
||||||
open class ChunkEntity(var prevToken: String? = null,
|
open class ChunkEntity(var prevToken: String? = null,
|
||||||
var nextToken: String? = null,
|
var nextToken: String? = null,
|
||||||
var isLimited: Boolean = true,
|
|
||||||
var events: RealmList<EventEntity> = RealmList()
|
var events: RealmList<EventEntity> = RealmList()
|
||||||
) : RealmObject() {
|
) : RealmObject() {
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ fun ChunkEntity.Companion.findWithNextToken(realm: Realm, roomId: String, nextTo
|
|||||||
.findFirst()
|
.findFirst()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ChunkEntity.Companion.findLastFromRoom(realm: Realm, roomId: String): ChunkEntity? {
|
fun ChunkEntity.Companion.findLastLiveChunkFromRoom(realm: Realm, roomId: String): ChunkEntity? {
|
||||||
return where(realm, roomId)
|
return where(realm, roomId)
|
||||||
.and()
|
.and()
|
||||||
.isNull("nextToken")
|
.isNull("nextToken")
|
||||||
|
@ -5,6 +5,7 @@ import im.vector.matrix.android.internal.database.model.EventEntity
|
|||||||
import io.realm.Realm
|
import io.realm.Realm
|
||||||
import io.realm.RealmQuery
|
import io.realm.RealmQuery
|
||||||
import io.realm.RealmResults
|
import io.realm.RealmResults
|
||||||
|
import io.realm.Sort
|
||||||
|
|
||||||
fun EventEntity.Companion.where(realm: Realm, roomId: String): RealmQuery<EventEntity> {
|
fun EventEntity.Companion.where(realm: Realm, roomId: String): RealmQuery<EventEntity> {
|
||||||
return realm.where(EventEntity::class.java)
|
return realm.where(EventEntity::class.java)
|
||||||
@ -23,9 +24,9 @@ fun EventEntity.Companion.where(realm: Realm, chunk: ChunkEntity?): RealmQuery<E
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun RealmResults<EventEntity>.getLast(type: String? = null): EventEntity? {
|
fun RealmResults<EventEntity>.getLast(type: String? = null): EventEntity? {
|
||||||
var query = this.where()
|
var query = this.where().sort("originServerTs", Sort.DESCENDING)
|
||||||
if (type != null) {
|
if (type != null) {
|
||||||
query = query.equalTo("type", type)
|
query = query.equalTo("type", type)
|
||||||
}
|
}
|
||||||
return query.findAll().last(null)
|
return query.findFirst()
|
||||||
}
|
}
|
@ -11,6 +11,7 @@ import im.vector.matrix.android.internal.database.model.ChunkEntity
|
|||||||
import im.vector.matrix.android.internal.database.query.where
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
import im.vector.matrix.android.internal.session.room.timeline.PaginationRequest
|
import im.vector.matrix.android.internal.session.room.timeline.PaginationRequest
|
||||||
import im.vector.matrix.android.internal.session.room.timeline.TimelineBoundaryCallback
|
import im.vector.matrix.android.internal.session.room.timeline.TimelineBoundaryCallback
|
||||||
|
import io.realm.Sort
|
||||||
import org.koin.standalone.KoinComponent
|
import org.koin.standalone.KoinComponent
|
||||||
import org.koin.standalone.inject
|
import org.koin.standalone.inject
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
@ -28,17 +29,16 @@ data class DefaultRoom(
|
|||||||
ChunkEntity.where(realm, roomId)
|
ChunkEntity.where(realm, roomId)
|
||||||
.findAll()
|
.findAll()
|
||||||
.last(null)
|
.last(null)
|
||||||
?.let { it.events }
|
?.let {
|
||||||
?.where()
|
it.events.where().sort("originServerTs", Sort.DESCENDING)
|
||||||
?.sort("originServerTs")
|
}
|
||||||
}
|
}
|
||||||
val domainSourceFactory = realmDataSourceFactory.map { EventMapper.map(it) }
|
val domainSourceFactory = realmDataSourceFactory.map { EventMapper.map(it) }
|
||||||
|
|
||||||
val pagedListConfig = PagedList.Config.Builder()
|
val pagedListConfig = PagedList.Config.Builder()
|
||||||
.setEnablePlaceholders(false)
|
.setEnablePlaceholders(false)
|
||||||
.setPageSize(10)
|
.setPageSize(10)
|
||||||
.setInitialLoadSizeHint(30)
|
.setPrefetchDistance(10)
|
||||||
.setPrefetchDistance(5)
|
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
val livePagedListBuilder = LivePagedListBuilder(domainSourceFactory, pagedListConfig).setBoundaryCallback(boundaryCallback)
|
val livePagedListBuilder = LivePagedListBuilder(domainSourceFactory, pagedListConfig).setBoundaryCallback(boundaryCallback)
|
||||||
|
@ -7,6 +7,7 @@ import com.zhuinden.monarchy.Monarchy
|
|||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.failure.Failure
|
import im.vector.matrix.android.api.failure.Failure
|
||||||
import im.vector.matrix.android.api.util.Cancelable
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
|
import im.vector.matrix.android.internal.database.DBConstants
|
||||||
import im.vector.matrix.android.internal.database.mapper.asEntity
|
import im.vector.matrix.android.internal.database.mapper.asEntity
|
||||||
import im.vector.matrix.android.internal.database.model.ChunkEntity
|
import im.vector.matrix.android.internal.database.model.ChunkEntity
|
||||||
import im.vector.matrix.android.internal.database.model.RoomEntity
|
import im.vector.matrix.android.internal.database.model.RoomEntity
|
||||||
@ -14,6 +15,7 @@ import im.vector.matrix.android.internal.database.query.findAllIncludingEvents
|
|||||||
import im.vector.matrix.android.internal.database.query.findWithNextToken
|
import im.vector.matrix.android.internal.database.query.findWithNextToken
|
||||||
import im.vector.matrix.android.internal.database.query.findWithPrevToken
|
import im.vector.matrix.android.internal.database.query.findWithPrevToken
|
||||||
import im.vector.matrix.android.internal.database.query.where
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
|
import im.vector.matrix.android.internal.legacy.util.FilterUtil
|
||||||
import im.vector.matrix.android.internal.network.executeRequest
|
import im.vector.matrix.android.internal.network.executeRequest
|
||||||
import im.vector.matrix.android.internal.session.room.RoomAPI
|
import im.vector.matrix.android.internal.session.room.RoomAPI
|
||||||
import im.vector.matrix.android.internal.session.room.model.PaginationDirection
|
import im.vector.matrix.android.internal.session.room.model.PaginationDirection
|
||||||
@ -32,10 +34,10 @@ class PaginationRequest(private val roomAPI: RoomAPI,
|
|||||||
from: String?,
|
from: String?,
|
||||||
direction: PaginationDirection,
|
direction: PaginationDirection,
|
||||||
limit: Int = 10,
|
limit: Int = 10,
|
||||||
filter: String? = null,
|
|
||||||
callback: MatrixCallback<TokenChunkEvent>
|
callback: MatrixCallback<TokenChunkEvent>
|
||||||
): Cancelable {
|
): Cancelable {
|
||||||
val job = GlobalScope.launch(coroutineDispatchers.main) {
|
val job = GlobalScope.launch(coroutineDispatchers.main) {
|
||||||
|
val filter = FilterUtil.createRoomEventFilter(true)?.toJSONString()
|
||||||
val chunkOrFailure = execute(roomId, from, direction, limit, filter)
|
val chunkOrFailure = execute(roomId, from, direction, limit, filter)
|
||||||
chunkOrFailure.bimap({ callback.onFailure(it) }, { callback.onSuccess(it) })
|
chunkOrFailure.bimap({ callback.onFailure(it) }, { callback.onSuccess(it) })
|
||||||
}
|
}
|
||||||
@ -70,14 +72,13 @@ class PaginationRequest(private val roomAPI: RoomAPI,
|
|||||||
private fun insertInDb(chunkEvent: TokenChunkEvent, roomId: String) {
|
private fun insertInDb(chunkEvent: TokenChunkEvent, roomId: String) {
|
||||||
monarchy.runTransactionSync { realm ->
|
monarchy.runTransactionSync { realm ->
|
||||||
val roomEntity = RoomEntity.where(realm, roomId).findFirst()
|
val roomEntity = RoomEntity.where(realm, roomId).findFirst()
|
||||||
?: return@runTransactionSync
|
?: return@runTransactionSync
|
||||||
|
|
||||||
val nextChunk = ChunkEntity.findWithPrevToken(realm, roomId, chunkEvent.nextToken)
|
val nextChunk = ChunkEntity.findWithPrevToken(realm, roomId, chunkEvent.nextToken)
|
||||||
val prevChunk = ChunkEntity.findWithNextToken(realm, roomId, chunkEvent.prevToken)
|
val prevChunk = ChunkEntity.findWithNextToken(realm, roomId, chunkEvent.prevToken)
|
||||||
|
|
||||||
val mergedEvents = chunkEvent.chunk + chunkEvent.stateEvents
|
val eventIds = chunkEvent.chunk.filter { it.eventId != null }.map { it.eventId!! }
|
||||||
val mergedEventIds = mergedEvents.filter { it.eventId != null }.map { it.eventId!! }
|
val chunksOverlapped = ChunkEntity.findAllIncludingEvents(realm, eventIds)
|
||||||
val chunksOverlapped = ChunkEntity.findAllIncludingEvents(realm, mergedEventIds)
|
|
||||||
val hasOverlapped = chunksOverlapped.isNotEmpty()
|
val hasOverlapped = chunksOverlapped.isNotEmpty()
|
||||||
|
|
||||||
val currentChunk = if (nextChunk != null) {
|
val currentChunk = if (nextChunk != null) {
|
||||||
@ -85,9 +86,25 @@ class PaginationRequest(private val roomAPI: RoomAPI,
|
|||||||
} else {
|
} else {
|
||||||
ChunkEntity()
|
ChunkEntity()
|
||||||
}
|
}
|
||||||
|
|
||||||
currentChunk.prevToken = chunkEvent.prevToken
|
currentChunk.prevToken = chunkEvent.prevToken
|
||||||
mergedEvents.forEach { event ->
|
|
||||||
|
|
||||||
|
val stateEventsChunk = ChunkEntity.findWithNextToken(realm, roomId, DBConstants.STATE_EVENTS_CHUNK_TOKEN)
|
||||||
|
?: ChunkEntity().apply {
|
||||||
|
this.prevToken = DBConstants.STATE_EVENTS_CHUNK_TOKEN
|
||||||
|
this.nextToken = DBConstants.STATE_EVENTS_CHUNK_TOKEN
|
||||||
|
}
|
||||||
|
|
||||||
|
chunkEvent.stateEvents.forEach { stateEvent ->
|
||||||
|
val eventEntity = stateEvent.asEntity().let {
|
||||||
|
realm.copyToRealmOrUpdate(it)
|
||||||
|
}
|
||||||
|
if (!stateEventsChunk.events.contains(eventEntity)) {
|
||||||
|
stateEventsChunk.events.add(0, eventEntity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chunkEvent.chunk.forEach { event ->
|
||||||
val eventEntity = event.asEntity().let {
|
val eventEntity = event.asEntity().let {
|
||||||
realm.copyToRealmOrUpdate(it)
|
realm.copyToRealmOrUpdate(it)
|
||||||
}
|
}
|
||||||
@ -101,13 +118,14 @@ class PaginationRequest(private val roomAPI: RoomAPI,
|
|||||||
roomEntity.chunks.remove(prevChunk)
|
roomEntity.chunks.remove(prevChunk)
|
||||||
|
|
||||||
} else if (hasOverlapped) {
|
} else if (hasOverlapped) {
|
||||||
chunksOverlapped.forEach { chunk ->
|
chunksOverlapped.forEach { overlapped ->
|
||||||
chunk.events.forEach { event ->
|
overlapped.events.forEach { event ->
|
||||||
if (!currentChunk.events.contains(event)) {
|
if (!currentChunk.events.contains(event)) {
|
||||||
currentChunk.events.add(0, event)
|
currentChunk.events.add(0, event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
roomEntity.chunks.remove(chunk)
|
currentChunk.prevToken = overlapped.prevToken
|
||||||
|
roomEntity.chunks.remove(overlapped)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!roomEntity.chunks.contains(currentChunk)) {
|
if (!roomEntity.chunks.contains(currentChunk)) {
|
||||||
|
@ -32,7 +32,7 @@ class TimelineBoundaryCallback(private val paginationRequest: PaginationRequest,
|
|||||||
return@doWithRealm
|
return@doWithRealm
|
||||||
}
|
}
|
||||||
val chunkEntity = ChunkEntity.findAllIncludingEvents(realm, Collections.singletonList(itemAtEnd.eventId)).firstOrNull()
|
val chunkEntity = ChunkEntity.findAllIncludingEvents(realm, Collections.singletonList(itemAtEnd.eventId)).firstOrNull()
|
||||||
paginationRequest.execute(roomId, chunkEntity?.nextToken, PaginationDirection.FORWARDS, callback = createCallback(it))
|
paginationRequest.execute(roomId, chunkEntity?.prevToken, PaginationDirection.BACKWARDS, callback = createCallback(it))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -44,7 +44,7 @@ class TimelineBoundaryCallback(private val paginationRequest: PaginationRequest,
|
|||||||
return@doWithRealm
|
return@doWithRealm
|
||||||
}
|
}
|
||||||
val chunkEntity = ChunkEntity.findAllIncludingEvents(realm, Collections.singletonList(itemAtFront.eventId)).firstOrNull()
|
val chunkEntity = ChunkEntity.findAllIncludingEvents(realm, Collections.singletonList(itemAtFront.eventId)).firstOrNull()
|
||||||
paginationRequest.execute(roomId, chunkEntity?.prevToken, PaginationDirection.BACKWARDS, callback = createCallback(it))
|
paginationRequest.execute(roomId, chunkEntity?.nextToken, PaginationDirection.FORWARDS, callback = createCallback(it))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import im.vector.matrix.android.internal.database.mapper.asEntity
|
|||||||
import im.vector.matrix.android.internal.database.model.ChunkEntity
|
import im.vector.matrix.android.internal.database.model.ChunkEntity
|
||||||
import im.vector.matrix.android.internal.database.model.RoomEntity
|
import im.vector.matrix.android.internal.database.model.RoomEntity
|
||||||
import im.vector.matrix.android.internal.database.query.findAllIncludingEvents
|
import im.vector.matrix.android.internal.database.query.findAllIncludingEvents
|
||||||
import im.vector.matrix.android.internal.database.query.findLastFromRoom
|
import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom
|
||||||
import im.vector.matrix.android.internal.database.query.where
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
import im.vector.matrix.android.internal.session.sync.model.InvitedRoomSync
|
import im.vector.matrix.android.internal.session.sync.model.InvitedRoomSync
|
||||||
import im.vector.matrix.android.internal.session.sync.model.RoomSync
|
import im.vector.matrix.android.internal.session.sync.model.RoomSync
|
||||||
@ -95,14 +95,13 @@ class RoomSyncHandler(private val monarchy: Monarchy) {
|
|||||||
isLimited: Boolean = true): ChunkEntity {
|
isLimited: Boolean = true): ChunkEntity {
|
||||||
|
|
||||||
val chunkEntity = if (!isLimited) {
|
val chunkEntity = if (!isLimited) {
|
||||||
ChunkEntity.findLastFromRoom(realm, roomId)
|
ChunkEntity.findLastLiveChunkFromRoom(realm, roomId)
|
||||||
} else {
|
} else {
|
||||||
val eventIds = eventList.filter { it.eventId != null }.map { it.eventId!! }
|
val eventIds = eventList.filter { it.eventId != null }.map { it.eventId!! }
|
||||||
ChunkEntity.findAllIncludingEvents(realm, eventIds).firstOrNull()
|
ChunkEntity.findAllIncludingEvents(realm, eventIds).firstOrNull()
|
||||||
} ?: ChunkEntity().apply { this.prevToken = prevToken }
|
} ?: ChunkEntity().apply { this.prevToken = prevToken }
|
||||||
|
|
||||||
chunkEntity.nextToken = nextToken
|
chunkEntity.nextToken = nextToken
|
||||||
chunkEntity.isLimited = isLimited
|
|
||||||
|
|
||||||
eventList.forEach { event ->
|
eventList.forEach { event ->
|
||||||
val eventEntity = event.asEntity().let {
|
val eventEntity = event.asEntity().let {
|
||||||
|
Loading…
Reference in New Issue
Block a user