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.MatrixCallback
import im.vector.matrix.android.api.session.Session 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.events.model.Event
import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.rx.rx import im.vector.matrix.rx.rx
import im.vector.riotredesign.core.extensions.lastMinBy import im.vector.riotredesign.core.extensions.lastMinBy
import im.vector.riotredesign.core.platform.RiotViewModel import im.vector.riotredesign.core.platform.RiotViewModel
import im.vector.riotredesign.features.home.room.VisibleRoomHolder import im.vector.riotredesign.features.home.room.VisibleRoomHolder
import io.reactivex.rxkotlin.subscribeBy import io.reactivex.rxkotlin.subscribeBy
import org.koin.android.ext.android.get import org.koin.android.ext.android.get
import timber.log.Timber
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit


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


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


fun process(action: RoomDetailActions) { 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 METHODS *****************************************************************************


private fun handleSendMessage(action: RoomDetailActions.SendMessage) { 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.read.ReadService
import im.vector.matrix.android.api.session.room.send.SendService 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.session.room.timeline.TimelineService
import im.vector.matrix.android.api.util.Cancelable


/** /**
* This interface defines methods to interact within a room. * This interface defines methods to interact within a room.
@ -40,4 +39,26 @@ interface Room : TimelineService, SendService, ReadService, RoomMembersService {
*/ */
val roomSummary: LiveData<RoomSummary> 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>> fun readReceipts(): LiveData<List<ReadReceipt>>


/** /**
* @param the eventId to look for receipt. * @param eventId to look for receipt.
* * @return the receipt associated or null * * @return the receipt associated or null
*/ */
fun readReceipt(eventId: String): ReadReceipt? 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,16 +29,20 @@ 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.RoomSummaryEntity
import im.vector.matrix.android.internal.database.model.RoomSummaryEntityFields import im.vector.matrix.android.internal.database.model.RoomSummaryEntityFields
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.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, override val roomId: String,
private val monarchy: Monarchy, private val monarchy: Monarchy,
private val timelineService: TimelineService, private val timelineService: DefaultTimelineService,
private val sendService: SendService, private val sendService: DefaultSendService,
private val readService: ReadService, private val readService: DefaultReadService,
private val roomMembersService: RoomMembersService private val readServiceListeners: ReadServiceListeners,

private val roomMembersService: DefaultRoomMembersService

) : Room, ) : Room,
TimelineService by timelineService, TimelineService by timelineService,
SendService by sendService, SendService by sendService,
@ -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.LoadRoomMembersTask
import im.vector.matrix.android.internal.session.room.members.RoomMemberExtractor 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.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.read.SetReadMarkersTask
import im.vector.matrix.android.internal.session.room.send.DefaultSendService import im.vector.matrix.android.internal.session.room.send.DefaultSendService
import im.vector.matrix.android.internal.session.room.send.EventFactory 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 sendService = DefaultSendService(roomId, eventFactory, monarchy)
val roomMembersService = DefaultRoomMembersService(roomId, monarchy, loadRoomMembersTask, taskExecutor) val roomMembersService = DefaultRoomMembersService(roomId, monarchy, loadRoomMembersTask, taskExecutor)
val readService = DefaultReadService(roomId, monarchy, roomMembersService, setReadMarkersTask, taskExecutor) val readService = DefaultReadService(roomId, monarchy, roomMembersService, setReadMarkersTask, taskExecutor)
val readServiceListeners = ReadServiceListeners(roomId, monarchy)


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

View File

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


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


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

override fun readReceipts(): LiveData<List<ReadReceipt>> { override fun readReceipts(): LiveData<List<ReadReceipt>> {
return monarchy.findAllMappedWithChanges( return monarchy.findAllMappedWithChanges(
{ realm -> ReadReceiptEntity.where(realm, roomId) }, { realm -> ReadReceiptEntity.where(realm, roomId) },

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)
}


}