Merge pull request #637 from vector-im/feature/fix_room_summary

Feature/fix some room related stuff
This commit is contained in:
ganfra 2019-10-23 17:29:14 +02:00 committed by GitHub
commit b17b54d218
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 71 additions and 38 deletions

View File

@ -0,0 +1,28 @@
/*
* 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.api.session.events.model
import java.util.*
object LocalEcho {
private const val PREFIX = "local."
fun isLocalEchoId(eventId: String) = eventId.startsWith(PREFIX)
fun createLocalEchoId() = "${PREFIX}${UUID.randomUUID()}"
}

View File

@ -16,6 +16,7 @@
package im.vector.matrix.android.internal.database.query
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.session.events.model.LocalEcho
import im.vector.matrix.android.internal.database.model.ChunkEntity
import im.vector.matrix.android.internal.database.model.ReadReceiptEntity
@ -26,7 +27,9 @@ internal fun isEventRead(monarchy: Monarchy,
if (userId.isNullOrBlank() || roomId.isNullOrBlank() || eventId.isNullOrBlank()) {
return false
}
if (LocalEcho.isLocalEchoId(eventId)) {
return true
}
var isEventRead = false
monarchy.doWithRealm { realm ->

View File

@ -61,25 +61,28 @@ internal fun TimelineEventEntity.Companion.findWithSenderMembershipEvent(realm:
internal fun TimelineEventEntity.Companion.latestEvent(realm: Realm,
roomId: String,
includesSending: Boolean,
includedTypes: List<String> = emptyList(),
excludedTypes: List<String> = emptyList()): TimelineEventEntity? {
filterTypes: List<String> = emptyList()): TimelineEventEntity? {
val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: return null
val eventList = if (includesSending && roomEntity.sendingTimelineEvents.isNotEmpty()) {
roomEntity.sendingTimelineEvents
val sendingTimelineEvents = roomEntity.sendingTimelineEvents.where().filterTypes(filterTypes)
val liveEvents = ChunkEntity.findLastLiveChunkFromRoom(realm, roomId)?.timelineEvents?.where()?.filterTypes(filterTypes)
val query = if (includesSending && sendingTimelineEvents.findAll().isNotEmpty()) {
sendingTimelineEvents
} else {
ChunkEntity.findLastLiveChunkFromRoom(realm, roomId)?.timelineEvents
}
val query = eventList?.where()
if (includedTypes.isNotEmpty()) {
query?.`in`(TimelineEventEntityFields.ROOT.TYPE, includedTypes.toTypedArray())
} else if (excludedTypes.isNotEmpty()) {
query?.not()?.`in`(TimelineEventEntityFields.ROOT.TYPE, excludedTypes.toTypedArray())
liveEvents
}
return query
?.sort(TimelineEventEntityFields.ROOT.DISPLAY_INDEX, Sort.DESCENDING)
?.findFirst()
}
internal fun RealmQuery<TimelineEventEntity>.filterTypes(filterTypes: List<String>): RealmQuery<TimelineEventEntity> {
return if (filterTypes.isEmpty()) {
this
} else {
this.`in`(TimelineEventEntityFields.ROOT.TYPE, filterTypes.toTypedArray())
}
}
internal fun RealmQuery<TimelineEventEntity>.next(from: Int? = null, strict: Boolean = true): TimelineEventEntity? {
if (from != null) {
if (strict) {

View File

@ -28,7 +28,6 @@ import im.vector.matrix.android.internal.database.mapper.EventMapper
import im.vector.matrix.android.internal.database.model.*
import im.vector.matrix.android.internal.database.query.create
import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory
import im.vector.matrix.android.internal.task.Task
import im.vector.matrix.android.internal.util.awaitTransaction
import io.realm.Realm
@ -71,7 +70,7 @@ internal class DefaultEventRelationsAggregationTask @Inject constructor(
Timber.w("Event has no room id ${event.eventId}")
return@forEach
}
val isLocalEcho = LocalEchoEventFactory.isLocalEchoId(event.eventId ?: "")
val isLocalEcho = LocalEcho.isLocalEchoId(event.eventId ?: "")
when (event.type) {
EventType.REACTION -> {
// we got a reaction!!

View File

@ -87,7 +87,7 @@ internal class RoomSummaryUpdater @Inject constructor(@UserId private val userId
roomSummaryEntity.membership = membership
}
val latestPreviewableEvent = TimelineEventEntity.latestEvent(realm, roomId, includesSending = true, includedTypes = PREVIEWABLE_TYPES)
val latestPreviewableEvent = TimelineEventEntity.latestEvent(realm, roomId, includesSending = true, filterTypes = PREVIEWABLE_TYPES)
val lastTopicEvent = EventEntity.where(realm, roomId, EventType.STATE_ROOM_TOPIC).prev()?.asDomain()
roomSummaryEntity.hasUnreadMessages = roomSummaryEntity.notificationCount > 0

View File

@ -18,6 +18,7 @@ package im.vector.matrix.android.internal.session.room.prune
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.LocalEcho
import im.vector.matrix.android.api.session.events.model.UnsignedData
import im.vector.matrix.android.internal.database.helper.updateSenderData
import im.vector.matrix.android.internal.database.mapper.ContentMapper
@ -27,7 +28,6 @@ import im.vector.matrix.android.internal.database.model.TimelineEventEntity
import im.vector.matrix.android.internal.database.query.findWithSenderMembershipEvent
import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.di.MoshiProvider
import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory
import im.vector.matrix.android.internal.task.Task
import im.vector.matrix.android.internal.util.awaitTransaction
import io.realm.Realm
@ -59,7 +59,7 @@ internal class DefaultPruneEventTask @Inject constructor(private val monarchy: M
// Check that we know this event
EventEntity.where(realm, eventId = redactionEvent.eventId ?: "").findFirst() ?: return
val isLocalEcho = LocalEchoEventFactory.isLocalEchoId(redactionEvent.eventId ?: "")
val isLocalEcho = LocalEcho.isLocalEchoId(redactionEvent.eventId ?: "")
Timber.v("Redact event for ${redactionEvent.redacts} localEcho=$isLocalEcho")
val eventToPrune = EventEntity.where(realm, eventId = redactionEvent.redacts).findFirst()

View File

@ -17,6 +17,7 @@
package im.vector.matrix.android.internal.session.room.read
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.session.events.model.LocalEcho
import im.vector.matrix.android.internal.database.model.ReadMarkerEntity
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
@ -26,7 +27,6 @@ import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.di.UserId
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.send.LocalEchoEventFactory
import im.vector.matrix.android.internal.session.sync.ReadReceiptHandler
import im.vector.matrix.android.internal.session.sync.RoomFullyReadHandler
import im.vector.matrix.android.internal.task.Task
@ -73,7 +73,7 @@ internal class DefaultSetReadMarkersTask @Inject constructor(private val roomAPI
}
if (fullyReadEventId != null && isReadMarkerMoreRecent(params.roomId, fullyReadEventId)) {
if (LocalEchoEventFactory.isLocalEchoId(fullyReadEventId)) {
if (LocalEcho.isLocalEchoId(fullyReadEventId)) {
Timber.w("Can't set read marker for local event $fullyReadEventId")
} else {
markers[READ_MARKER] = fullyReadEventId
@ -82,7 +82,7 @@ internal class DefaultSetReadMarkersTask @Inject constructor(private val roomAPI
if (readReceiptEventId != null
&& !isEventRead(monarchy, userId, params.roomId, readReceiptEventId)) {
if (LocalEchoEventFactory.isLocalEchoId(readReceiptEventId)) {
if (LocalEcho.isLocalEchoId(readReceiptEventId)) {
Timber.w("Can't set read receipt for local event $readReceiptEventId")
} else {
markers[READ_RECEIPT] = readReceiptEventId

View File

@ -160,7 +160,7 @@ internal class LocalEchoEventFactory @Inject constructor(@UserId private val use
reaction
)
)
val localId = dummyEventId()
val localId = LocalEcho.createLocalEchoId()
return Event(
roomId = roomId,
originServerTs = dummyOriginServerTs(),
@ -264,7 +264,7 @@ internal class LocalEchoEventFactory @Inject constructor(@UserId private val use
}
private fun createEvent(roomId: String, content: Any? = null): Event {
val localID = dummyEventId()
val localID = LocalEcho.createLocalEchoId()
return Event(
roomId = roomId,
originServerTs = dummyOriginServerTs(),
@ -280,10 +280,6 @@ internal class LocalEchoEventFactory @Inject constructor(@UserId private val use
return System.currentTimeMillis()
}
private fun dummyEventId(): String {
return "$LOCAL_ID_PREFIX${UUID.randomUUID()}"
}
fun createReplyTextEvent(roomId: String, eventReplied: TimelineEvent, replyText: String, autoMarkdown: Boolean): Event? {
// Fallbacks and event representation
// TODO Add error/warning logs when any of this is null
@ -383,7 +379,7 @@ internal class LocalEchoEventFactory @Inject constructor(@UserId private val use
}
*/
fun createRedactEvent(roomId: String, eventId: String, reason: String?): Event {
val localID = dummyEventId()
val localID = LocalEcho.createLocalEchoId()
return Event(
roomId = roomId,
originServerTs = dummyOriginServerTs(),
@ -407,8 +403,6 @@ internal class LocalEchoEventFactory @Inject constructor(@UserId private val use
}
companion object {
const val LOCAL_ID_PREFIX = "local."
// <mx-reply>
// <blockquote>
// <a href="https://matrix.to/#/!somewhere:domain.com/$event:domain.com">In reply to</a>
@ -419,7 +413,5 @@ internal class LocalEchoEventFactory @Inject constructor(@UserId private val use
// </mx-reply>
// No whitespace because currently breaks temporary formatted text to Span
const val REPLY_PATTERN = """<mx-reply><blockquote><a href="%s">%s</a><a href="%s">%s</a><br />%s</blockquote></mx-reply>%s"""
fun isLocalEchoId(eventId: String): Boolean = eventId.startsWith(LOCAL_ID_PREFIX)
}
}

View File

@ -17,11 +17,12 @@
package im.vector.riotx.core.extensions
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.room.send.SendState
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
fun TimelineEvent.canReact(): Boolean {
// Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment
return root.getClearType() == EventType.MESSAGE && root.sendState.isSent() && !root.isRedacted()
return root.getClearType() == EventType.MESSAGE && root.sendState == SendState.SYNCED && !root.isRedacted()
}
fun TimelineEvent.displayReadMarker(myUserId: String): Boolean {

View File

@ -26,7 +26,7 @@ import android.view.animation.AnimationUtils
import im.vector.riotx.R
import kotlinx.coroutines.*
private const val DELAY_IN_MS = 1_500L
private const val DELAY_IN_MS = 1_000L
class ReadMarkerView @JvmOverloads constructor(
context: Context,

View File

@ -59,6 +59,7 @@ import im.vector.matrix.android.api.permalinks.PermalinkFactory
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.content.ContentAttachmentData
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.events.model.LocalEcho
import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.model.message.*
import im.vector.matrix.android.api.session.room.send.SendState
@ -994,10 +995,16 @@ class RoomDetailFragment :
return
}
val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition()
val firstVisibleItem = timelineEventController.adapter.getModelAtPosition(firstVisibleItemPosition)
val nextReadMarkerId = when (firstVisibleItem) {
is BaseEventItem -> firstVisibleItem.getEventIds().firstOrNull()
else -> null
var nextReadMarkerId: String? = null
for (itemPosition in firstVisibleItemPosition until lastVisibleItemPosition) {
val timelineItem = timelineEventController.adapter.getModelAtPosition(itemPosition)
if (timelineItem is BaseEventItem) {
val eventId = timelineItem.getEventIds().firstOrNull() ?: continue
if (!LocalEcho.isLocalEchoId(eventId)) {
nextReadMarkerId = eventId
break
}
}
}
if (nextReadMarkerId != null) {
roomDetailViewModel.process(RoomDetailActions.SetReadMarkerAction(nextReadMarkerId))

View File

@ -201,7 +201,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
if (canCancel(event)) {
add(SimpleAction.Cancel(eventId))
}
} else {
} else if (event.root.sendState == SendState.SYNCED) {
if (!event.root.isRedacted()) {
if (canReply(event, messageContent)) {
add(SimpleAction.Reply(eventId))