Read: allow setting read marker and read receipt to latest known event independently
This commit is contained in:
parent
d93050240a
commit
76065ac4fc
|
@ -26,10 +26,16 @@ import im.vector.matrix.android.api.util.Optional
|
||||||
*/
|
*/
|
||||||
interface ReadService {
|
interface ReadService {
|
||||||
|
|
||||||
|
enum class MarkAsReadParams{
|
||||||
|
READ_RECEIPT,
|
||||||
|
READ_MARKER,
|
||||||
|
BOTH
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Force the read marker to be set on the latest event.
|
* Force the read marker to be set on the latest event.
|
||||||
*/
|
*/
|
||||||
fun markAllAsRead(callback: MatrixCallback<Unit>)
|
fun markAsRead(params: MarkAsReadParams = MarkAsReadParams.BOTH, callback: MatrixCallback<Unit>)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the read receipt on the event with provided eventId.
|
* Set the read receipt on the event with provided eventId.
|
||||||
|
|
|
@ -88,7 +88,7 @@ internal class DefaultCreateRoomTask @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun setReadMarkers(roomId: String) {
|
private suspend fun setReadMarkers(roomId: String) {
|
||||||
val setReadMarkerParams = SetReadMarkersTask.Params(roomId, markAllAsRead = true)
|
val setReadMarkerParams = SetReadMarkersTask.Params(roomId, forceReadReceipt = true, forceReadMarker = true)
|
||||||
return readMarkersTask.execute(setReadMarkerParams)
|
return readMarkersTask.execute(setReadMarkerParams)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ internal class DefaultJoinRoomTask @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun setReadMarkers(roomId: String) {
|
private suspend fun setReadMarkers(roomId: String) {
|
||||||
val setReadMarkerParams = SetReadMarkersTask.Params(roomId, markAllAsRead = true)
|
val setReadMarkerParams = SetReadMarkersTask.Params(roomId, forceReadMarker = true, forceReadReceipt = true)
|
||||||
readMarkersTask.execute(setReadMarkerParams)
|
readMarkersTask.execute(setReadMarkerParams)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,10 +50,14 @@ internal class DefaultReadService @AssistedInject constructor(
|
||||||
fun create(roomId: String): ReadService
|
fun create(roomId: String): ReadService
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun markAllAsRead(callback: MatrixCallback<Unit>) {
|
override fun markAsRead(params: ReadService.MarkAsReadParams, callback: MatrixCallback<Unit>) {
|
||||||
val params = SetReadMarkersTask.Params(roomId, markAllAsRead = true)
|
val taskParams = SetReadMarkersTask.Params(
|
||||||
|
roomId = roomId,
|
||||||
|
forceReadMarker = params.forceReadMarker(),
|
||||||
|
forceReadReceipt = params.forceReadReceipt()
|
||||||
|
)
|
||||||
setReadMarkersTask
|
setReadMarkersTask
|
||||||
.configureWith(params) {
|
.configureWith(taskParams) {
|
||||||
this.callback = callback
|
this.callback = callback
|
||||||
}
|
}
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
|
@ -110,4 +114,13 @@ internal class DefaultReadService @AssistedInject constructor(
|
||||||
it.firstOrNull() ?: emptyList()
|
it.firstOrNull() ?: emptyList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun ReadService.MarkAsReadParams.forceReadMarker(): Boolean {
|
||||||
|
return this == ReadService.MarkAsReadParams.READ_MARKER || this == ReadService.MarkAsReadParams.BOTH
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ReadService.MarkAsReadParams.forceReadReceipt(): Boolean {
|
||||||
|
return this == ReadService.MarkAsReadParams.READ_RECEIPT || this == ReadService.MarkAsReadParams.BOTH
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ internal class DefaultMarkAllRoomsReadTask @Inject constructor(private val readM
|
||||||
|
|
||||||
override suspend fun execute(params: MarkAllRoomsReadTask.Params) {
|
override suspend fun execute(params: MarkAllRoomsReadTask.Params) {
|
||||||
params.roomIds.forEach { roomId ->
|
params.roomIds.forEach { roomId ->
|
||||||
readMarkersTask.execute(SetReadMarkersTask.Params(roomId, markAllAsRead = true))
|
readMarkersTask.execute(SetReadMarkersTask.Params(roomId, forceReadMarker = true, forceReadReceipt = true))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import im.vector.matrix.android.internal.database.query.isReadMarkerMoreRecent
|
||||||
import im.vector.matrix.android.internal.database.query.latestEvent
|
import im.vector.matrix.android.internal.database.query.latestEvent
|
||||||
import im.vector.matrix.android.internal.database.query.where
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
import im.vector.matrix.android.internal.di.UserId
|
import im.vector.matrix.android.internal.di.UserId
|
||||||
|
import im.vector.matrix.android.internal.network.NetworkConnectivityChecker
|
||||||
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.sync.ReadReceiptHandler
|
import im.vector.matrix.android.internal.session.sync.ReadReceiptHandler
|
||||||
|
@ -35,16 +36,16 @@ import io.realm.Realm
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.collections.HashMap
|
|
||||||
import kotlin.collections.set
|
import kotlin.collections.set
|
||||||
|
|
||||||
internal interface SetReadMarkersTask : Task<SetReadMarkersTask.Params, Unit> {
|
internal interface SetReadMarkersTask : Task<SetReadMarkersTask.Params, Unit> {
|
||||||
|
|
||||||
data class Params(
|
data class Params(
|
||||||
val roomId: String,
|
val roomId: String,
|
||||||
val markAllAsRead: Boolean = false,
|
|
||||||
val fullyReadEventId: String? = null,
|
val fullyReadEventId: String? = null,
|
||||||
val readReceiptEventId: String? = null
|
val readReceiptEventId: String? = null,
|
||||||
|
val forceReadReceipt: Boolean = false,
|
||||||
|
val forceReadMarker: Boolean = false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,22 +58,24 @@ internal class DefaultSetReadMarkersTask @Inject constructor(
|
||||||
private val roomFullyReadHandler: RoomFullyReadHandler,
|
private val roomFullyReadHandler: RoomFullyReadHandler,
|
||||||
private val readReceiptHandler: ReadReceiptHandler,
|
private val readReceiptHandler: ReadReceiptHandler,
|
||||||
@UserId private val userId: String,
|
@UserId private val userId: String,
|
||||||
private val eventBus: EventBus
|
private val eventBus: EventBus,
|
||||||
|
private val networkConnectivityChecker: NetworkConnectivityChecker
|
||||||
) : SetReadMarkersTask {
|
) : SetReadMarkersTask {
|
||||||
|
|
||||||
override suspend fun execute(params: SetReadMarkersTask.Params) {
|
override suspend fun execute(params: SetReadMarkersTask.Params) {
|
||||||
val markers = HashMap<String, String>()
|
val markers = HashMap<String, String>()
|
||||||
|
|
||||||
Timber.v("Execute set read marker with params: $params")
|
Timber.v("Execute set read marker with params: $params")
|
||||||
val (fullyReadEventId, readReceiptEventId) = if (params.markAllAsRead) {
|
val latestSyncedEventId = latestSyncedEventId(params.roomId)
|
||||||
val latestSyncedEventId = Realm.getInstance(monarchy.realmConfiguration).use { realm ->
|
val fullyReadEventId = if(params.forceReadMarker){
|
||||||
TimelineEventEntity.latestEvent(realm, roomId = params.roomId, includesSending = false)?.eventId
|
latestSyncedEventId
|
||||||
}
|
}else {
|
||||||
Pair(latestSyncedEventId, latestSyncedEventId)
|
params.fullyReadEventId
|
||||||
} else {
|
}
|
||||||
Pair(params.fullyReadEventId, params.readReceiptEventId)
|
val readReceiptEventId = if(params.forceReadReceipt){
|
||||||
|
latestSyncedEventId
|
||||||
|
}else {
|
||||||
|
params.readReceiptEventId
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fullyReadEventId != null && !isReadMarkerMoreRecent(monarchy, params.roomId, fullyReadEventId)) {
|
if (fullyReadEventId != null && !isReadMarkerMoreRecent(monarchy, params.roomId, fullyReadEventId)) {
|
||||||
if (LocalEcho.isLocalEchoId(fullyReadEventId)) {
|
if (LocalEcho.isLocalEchoId(fullyReadEventId)) {
|
||||||
Timber.w("Can't set read marker for local event $fullyReadEventId")
|
Timber.w("Can't set read marker for local event $fullyReadEventId")
|
||||||
|
@ -80,7 +83,6 @@ internal class DefaultSetReadMarkersTask @Inject constructor(
|
||||||
markers[READ_MARKER] = fullyReadEventId
|
markers[READ_MARKER] = fullyReadEventId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (readReceiptEventId != null
|
if (readReceiptEventId != null
|
||||||
&& !isEventRead(monarchy, userId, params.roomId, readReceiptEventId)) {
|
&& !isEventRead(monarchy, userId, params.roomId, readReceiptEventId)) {
|
||||||
if (LocalEcho.isLocalEchoId(readReceiptEventId)) {
|
if (LocalEcho.isLocalEchoId(readReceiptEventId)) {
|
||||||
|
@ -89,16 +91,24 @@ internal class DefaultSetReadMarkersTask @Inject constructor(
|
||||||
markers[READ_RECEIPT] = readReceiptEventId
|
markers[READ_RECEIPT] = readReceiptEventId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val shouldUpdateRoomSummary = readReceiptEventId != null && readReceiptEventId == latestSyncedEventId
|
||||||
|
updateDatabase(params.roomId, markers, shouldUpdateRoomSummary)
|
||||||
if (markers.isEmpty()) {
|
if (markers.isEmpty()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
updateDatabase(params.roomId, markers)
|
networkConnectivityChecker.waitUntilConnected()
|
||||||
executeRequest<Unit>(eventBus) {
|
executeRequest<Unit>(eventBus) {
|
||||||
apiCall = roomAPI.sendReadMarker(params.roomId, markers)
|
apiCall = roomAPI.sendReadMarker(params.roomId, markers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun updateDatabase(roomId: String, markers: HashMap<String, String>) {
|
private fun latestSyncedEventId(roomId: String): String? =
|
||||||
|
Realm.getInstance(monarchy.realmConfiguration).use { realm ->
|
||||||
|
TimelineEventEntity.latestEvent(realm, roomId = roomId, includesSending = false)?.eventId
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun updateDatabase(roomId: String, markers: HashMap<String, String>, shouldUpdateRoomSummary: Boolean) {
|
||||||
monarchy.awaitTransaction { realm ->
|
monarchy.awaitTransaction { realm ->
|
||||||
val readMarkerId = markers[READ_MARKER]
|
val readMarkerId = markers[READ_MARKER]
|
||||||
val readReceiptId = markers[READ_RECEIPT]
|
val readReceiptId = markers[READ_RECEIPT]
|
||||||
|
@ -108,14 +118,13 @@ internal class DefaultSetReadMarkersTask @Inject constructor(
|
||||||
if (readReceiptId != null) {
|
if (readReceiptId != null) {
|
||||||
val readReceiptContent = ReadReceiptHandler.createContent(userId, readReceiptId)
|
val readReceiptContent = ReadReceiptHandler.createContent(userId, readReceiptId)
|
||||||
readReceiptHandler.handle(realm, roomId, readReceiptContent, false)
|
readReceiptHandler.handle(realm, roomId, readReceiptContent, false)
|
||||||
val isLatestReceived = TimelineEventEntity.latestEvent(realm, roomId = roomId, includesSending = false)?.eventId == readReceiptId
|
}
|
||||||
if (isLatestReceived) {
|
if(shouldUpdateRoomSummary){
|
||||||
val roomSummary = RoomSummaryEntity.where(realm, roomId).findFirst()
|
val roomSummary = RoomSummaryEntity.where(realm, roomId).findFirst()
|
||||||
?: return@awaitTransaction
|
?: return@awaitTransaction
|
||||||
roomSummary.notificationCount = 0
|
roomSummary.notificationCount = 0
|
||||||
roomSummary.highlightCount = 0
|
roomSummary.highlightCount = 0
|
||||||
roomSummary.hasUnreadMessages = false
|
roomSummary.hasUnreadMessages = false
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@ import im.vector.matrix.android.api.session.room.model.message.MessageContent
|
||||||
import im.vector.matrix.android.api.session.room.model.message.MessageType
|
import im.vector.matrix.android.api.session.room.model.message.MessageType
|
||||||
import im.vector.matrix.android.api.session.room.model.message.getFileUrl
|
import im.vector.matrix.android.api.session.room.model.message.getFileUrl
|
||||||
import im.vector.matrix.android.api.session.room.model.tombstone.RoomTombstoneContent
|
import im.vector.matrix.android.api.session.room.model.tombstone.RoomTombstoneContent
|
||||||
|
import im.vector.matrix.android.api.session.room.read.ReadService
|
||||||
import im.vector.matrix.android.api.session.room.send.UserDraft
|
import im.vector.matrix.android.api.session.room.send.UserDraft
|
||||||
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
|
@ -149,6 +150,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
||||||
observeUnreadState()
|
observeUnreadState()
|
||||||
room.getRoomSummaryLive()
|
room.getRoomSummaryLive()
|
||||||
room.rx().loadRoomMembersIfNeeded().subscribeLogError().disposeOnClear()
|
room.rx().loadRoomMembersIfNeeded().subscribeLogError().disposeOnClear()
|
||||||
|
room.markAsRead(ReadService.MarkAsReadParams.READ_RECEIPT, object : MatrixCallback<Any> {})
|
||||||
// Inform the SDK that the room is displayed
|
// Inform the SDK that the room is displayed
|
||||||
session.onRoomDisplayed(initialState.roomId)
|
session.onRoomDisplayed(initialState.roomId)
|
||||||
}
|
}
|
||||||
|
@ -753,7 +755,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleMarkAllAsRead() {
|
private fun handleMarkAllAsRead() {
|
||||||
room.markAllAsRead(object : MatrixCallback<Any> {})
|
room.markAsRead(ReadService.MarkAsReadParams.BOTH, object : MatrixCallback<Any> {})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleReportContent(action: RoomDetailAction.ReportContent) {
|
private fun handleReportContent(action: RoomDetailAction.ReportContent) {
|
||||||
|
|
|
@ -23,6 +23,7 @@ import androidx.core.app.RemoteInput
|
||||||
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.room.Room
|
import im.vector.matrix.android.api.session.room.Room
|
||||||
|
import im.vector.matrix.android.api.session.room.read.ReadService
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.di.ActiveSessionHolder
|
import im.vector.riotx.core.di.ActiveSessionHolder
|
||||||
import im.vector.riotx.core.extensions.vectorComponent
|
import im.vector.riotx.core.extensions.vectorComponent
|
||||||
|
@ -88,7 +89,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
|
||||||
private fun handleMarkAsRead(roomId: String) {
|
private fun handleMarkAsRead(roomId: String) {
|
||||||
activeSessionHolder.getActiveSession().let { session ->
|
activeSessionHolder.getActiveSession().let { session ->
|
||||||
session.getRoom(roomId)
|
session.getRoom(roomId)
|
||||||
?.markAllAsRead(object : MatrixCallback<Unit> {})
|
?.markAsRead(ReadService.MarkAsReadParams.READ_RECEIPT, object : MatrixCallback<Unit> {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue