Read receipts : add a listener to know when data have been updated.

This commit is contained in:
ganfra 2019-02-01 12:38:33 +01:00
parent a5032920c2
commit 8d13f08574
7 changed files with 133 additions and 20 deletions

View File

@ -23,18 +23,20 @@ import im.vector.matrix.android.api.Matrix
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.rx.rx
import im.vector.riotredesign.core.extensions.lastMinBy
import im.vector.riotredesign.core.platform.RiotViewModel
import im.vector.riotredesign.features.home.room.VisibleRoomHolder
import io.reactivex.rxkotlin.subscribeBy
import org.koin.android.ext.android.get
import timber.log.Timber
import java.util.concurrent.TimeUnit

class RoomDetailViewModel(initialState: RoomDetailViewState,
private val session: Session,
private val visibleRoomHolder: VisibleRoomHolder
) : RiotViewModel<RoomDetailViewState>(initialState) {
) : RiotViewModel<RoomDetailViewState>(initialState), Room.Listener {

private val room = session.getRoom(initialState.roomId)!!
private val roomId = initialState.roomId
@ -57,6 +59,7 @@ class RoomDetailViewModel(initialState: RoomDetailViewState,
observeTimeline()
observeDisplayedEvents()
room.loadRoomMembersIfNeeded()
room.addListener(this)
}

fun process(action: RoomDetailActions) {
@ -67,6 +70,17 @@ class RoomDetailViewModel(initialState: RoomDetailViewState,
}
}

override fun onCleared() {
room.removeListener(this)
super.onCleared()
}

// Room.Listener *******************************************************************************

override fun onReadReceiptsUpdated() {
Timber.v("On read receipts updated")
}

// PRIVATE METHODS *****************************************************************************

private fun handleSendMessage(action: RoomDetailActions.SendMessage) {

View File

@ -22,7 +22,6 @@ import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.read.ReadService
import im.vector.matrix.android.api.session.room.send.SendService
import im.vector.matrix.android.api.session.room.timeline.TimelineService
import im.vector.matrix.android.api.util.Cancelable

/**
* This interface defines methods to interact within a room.
@ -40,4 +39,26 @@ interface Room : TimelineService, SendService, ReadService, RoomMembersService {
*/
val roomSummary: LiveData<RoomSummary>

/**
* Add a listener to the room.
* @param listener the listener to add.
*/
fun addListener(listener: Listener)

/**
* Remove a listener from the room.
* @param listener the listener to remove.
*/
fun removeListener(listener: Listener)

/**
* A listener defined at the room level to listen for some events from the different room services.
*/
interface Listener : ReadService.Listener {

override fun onReadReceiptsUpdated() {
// no-op
}
}

}

View File

@ -46,9 +46,19 @@ interface ReadService {
fun readReceipts(): LiveData<List<ReadReceipt>>

/**
* @param the eventId to look for receipt.
* @param eventId to look for receipt.
* * @return the receipt associated or null
*/
fun readReceipt(eventId: String): ReadReceipt?

/**
* A listener defined at the room level to listen for some read events.
*/
interface Listener {
/**
* Called when read receipts are updated in this room.
*/
fun onReadReceiptsUpdated()
}

}

View File

@ -29,21 +29,25 @@ import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
import im.vector.matrix.android.internal.database.model.RoomSummaryEntityFields
import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.session.room.members.DefaultRoomMembersService
import im.vector.matrix.android.internal.session.room.read.DefaultReadService
import im.vector.matrix.android.internal.session.room.read.ReadServiceListeners
import im.vector.matrix.android.internal.session.room.send.DefaultSendService
import im.vector.matrix.android.internal.session.room.timeline.DefaultTimelineService

internal data class DefaultRoom(
internal class DefaultRoom(
override val roomId: String,
private val monarchy: Monarchy,
private val timelineService: TimelineService,
private val sendService: SendService,
private val readService: ReadService,
private val roomMembersService: RoomMembersService


private val timelineService: DefaultTimelineService,
private val sendService: DefaultSendService,
private val readService: DefaultReadService,
private val readServiceListeners: ReadServiceListeners,
private val roomMembersService: DefaultRoomMembersService
) : Room,
TimelineService by timelineService,
SendService by sendService,
ReadService by readService,
RoomMembersService by roomMembersService {
TimelineService by timelineService,
SendService by sendService,
ReadService by readService,
RoomMembersService by roomMembersService {

override val roomSummary: LiveData<RoomSummary> by lazy {
val liveData = monarchy
@ -56,5 +60,12 @@ internal data class DefaultRoom(
}
}

override fun addListener(listener: Room.Listener) {
readServiceListeners.addListener(listener)
}

override fun removeListener(listener: Room.Listener) {
readServiceListeners.removeListener(listener)
}

}

View File

@ -22,6 +22,7 @@ import im.vector.matrix.android.internal.session.room.members.DefaultRoomMembers
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.read.DefaultReadService
import im.vector.matrix.android.internal.session.room.read.ReadServiceListeners
import im.vector.matrix.android.internal.session.room.read.SetReadMarkersTask
import im.vector.matrix.android.internal.session.room.send.DefaultSendService
import im.vector.matrix.android.internal.session.room.send.EventFactory
@ -49,6 +50,7 @@ internal class RoomFactory(private val loadRoomMembersTask: LoadRoomMembersTask,
val sendService = DefaultSendService(roomId, eventFactory, monarchy)
val roomMembersService = DefaultRoomMembersService(roomId, monarchy, loadRoomMembersTask, taskExecutor)
val readService = DefaultReadService(roomId, monarchy, roomMembersService, setReadMarkersTask, taskExecutor)
val readServiceListeners = ReadServiceListeners(roomId, monarchy)

return DefaultRoom(
roomId,
@ -56,6 +58,7 @@ internal class RoomFactory(private val loadRoomMembersTask: LoadRoomMembersTask,
timelineService,
sendService,
readService,
readServiceListeners,
roomMembersService
)
}

View File

@ -37,7 +37,7 @@ internal class DefaultReadService(private val roomId: String,
private val taskExecutor: TaskExecutor) : ReadService {

override fun markAllAsRead(callback: MatrixCallback<Void>) {
val latestEvent = getLatestEvent()
val latestEvent = monarchy.fetchCopied { EventEntity.latestEvent(it, roomId) }
val params = SetReadMarkersTask.Params(roomId, fullyReadEventId = latestEvent?.eventId, readReceiptEventId = latestEvent?.eventId)
setReadMarkersTask.configureWith(params).executeBy(taskExecutor)
}
@ -52,10 +52,6 @@ internal class DefaultReadService(private val roomId: String,
setReadMarkersTask.configureWith(params).executeBy(taskExecutor)
}

private fun getLatestEvent(): EventEntity? {
return monarchy.fetchCopied { EventEntity.latestEvent(it, roomId) }
}

override fun readReceipts(): LiveData<List<ReadReceipt>> {
return monarchy.findAllMappedWithChanges(
{ realm -> ReadReceiptEntity.where(realm, roomId) },
@ -68,7 +64,7 @@ internal class DefaultReadService(private val roomId: String,

override fun readReceipt(eventId: String): ReadReceipt? {
val readReceipt = monarchy.fetchCopied { ReadReceiptEntity.where(it, roomId).findFirst() }
?: return null
?: return null
val roomMember = roomMembersService.getRoomMember(readReceipt.userId)
return ReadReceipt(roomMember, readReceipt.eventId, readReceipt.originServerTs.toLong())
}

View File

@ -0,0 +1,58 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package im.vector.matrix.android.internal.session.room.read

import androidx.lifecycle.Observer
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.session.room.read.ReadService
import im.vector.matrix.android.internal.database.model.ReadReceiptEntity
import im.vector.matrix.android.internal.database.query.where

class ReadServiceListeners(private val roomId: String,
private val monarchy: Monarchy
) {

private val listeners = ArrayList<ReadService.Listener>()
private val readReceiptsLive by lazy {
monarchy.findAllManagedWithChanges { realm -> ReadReceiptEntity.where(realm, roomId) }
}
private val readReceiptLiveObserver = Observer<Monarchy.ManagedChangeSet<ReadReceiptEntity>> { listeners.forEach { it.onReadReceiptsUpdated() } }

fun addListener(listener: ReadService.Listener) {
if (listeners.isEmpty()) {
startObservingData()
}
listeners.add(listener)
}

fun removeListener(listener: ReadService.Listener) {
listeners.remove(listener)
if (listeners.isEmpty()) {
stopObservingData()
}
}

private fun startObservingData() {
readReceiptsLive.observeForever(readReceiptLiveObserver)
}

private fun stopObservingData() {
readReceiptsLive.removeObserver(readReceiptLiveObserver)
}


}