mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-03-18 20:29:10 +03:00
Merge pull request #2940 from vector-im/feature/bma/various_fixies
Rework event edition management
This commit is contained in:
commit
e5656e264a
29 changed files with 317 additions and 281 deletions
|
@ -30,7 +30,7 @@ Test:
|
|||
-
|
||||
|
||||
Other changes:
|
||||
-
|
||||
- Rework edition of event management
|
||||
|
||||
Changes in Element 1.1.0 (2021-02-19)
|
||||
===================================================
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/design_default_color_primary">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/testPage"
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="1"
|
||||
android:textSize="80sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
|
||||
</RelativeLayout>
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* 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 org.matrix.android.sdk.api.crypto
|
||||
|
||||
enum class VerificationState {
|
||||
REQUEST,
|
||||
WAITING,
|
||||
CANCELED_BY_ME,
|
||||
CANCELED_BY_OTHER,
|
||||
DONE
|
||||
}
|
||||
|
||||
fun VerificationState.isCanceled(): Boolean {
|
||||
return this == VerificationState.CANCELED_BY_ME || this == VerificationState.CANCELED_BY_OTHER
|
||||
}
|
|
@ -18,7 +18,7 @@ package org.matrix.android.sdk.api.session.room.model
|
|||
import org.matrix.android.sdk.api.session.events.model.Content
|
||||
|
||||
data class EditAggregatedSummary(
|
||||
val aggregatedContent: Content? = null,
|
||||
val latestContent: Content? = null,
|
||||
// The list of the eventIDs used to build the summary (might be out of sync if chunked received from message chunk)
|
||||
val sourceEvents: List<String>,
|
||||
val localEchos: List<String>,
|
||||
|
|
|
@ -17,7 +17,7 @@ package org.matrix.android.sdk.api.session.room.model
|
|||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import org.matrix.android.sdk.internal.session.room.VerificationState
|
||||
import org.matrix.android.sdk.api.crypto.VerificationState
|
||||
|
||||
/**
|
||||
* Contains an aggregated summary info of the references.
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
package org.matrix.android.sdk.api.session.room.model.relation
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
|
@ -92,8 +91,11 @@ interface RelationService {
|
|||
|
||||
/**
|
||||
* Get the edit history of the given event
|
||||
* The return list will contain the original event and all the editions of this event, done by the
|
||||
* same sender, sorted in the reverse order (so the original event is the latest element, and the
|
||||
* latest edition is the first element of the list)
|
||||
*/
|
||||
fun fetchEditHistory(eventId: String, callback: MatrixCallback<List<Event>>)
|
||||
suspend fun fetchEditHistory(eventId: String): List<Event>
|
||||
|
||||
/**
|
||||
* Reply to an event in the timeline (must be in same room)
|
||||
|
|
|
@ -123,8 +123,7 @@ fun TimelineEvent.getLastMessageContent(): MessageContent? {
|
|||
return if (root.getClearType() == EventType.STICKER) {
|
||||
root.getClearContent().toModel<MessageStickerContent>()
|
||||
} else {
|
||||
annotations?.editSummary?.aggregatedContent?.toModel()
|
||||
?: root.getClearContent().toModel()
|
||||
(annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* 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 org.matrix.android.sdk.internal.crypto.verification
|
||||
|
||||
import org.matrix.android.sdk.api.crypto.VerificationState
|
||||
import org.matrix.android.sdk.api.crypto.isCanceled
|
||||
|
||||
// State transition with control
|
||||
internal fun VerificationState?.toState(newState: VerificationState): VerificationState {
|
||||
// Cancel is always prioritary ?
|
||||
// Eg id i found that mac or keys mismatch and send a cancel and the other send a done, i have to
|
||||
// consider as canceled
|
||||
if (newState.isCanceled()) {
|
||||
return newState
|
||||
}
|
||||
// never move out of cancel
|
||||
if (this?.isCanceled() == true) {
|
||||
return this
|
||||
}
|
||||
return newState
|
||||
}
|
|
@ -18,6 +18,8 @@ package org.matrix.android.sdk.internal.database
|
|||
|
||||
import io.realm.DynamicRealm
|
||||
import io.realm.RealmMigration
|
||||
import org.matrix.android.sdk.internal.database.model.EditAggregatedSummaryEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.EditionOfEventFields
|
||||
import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.PendingThreePidEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.PreviewUrlCacheEntityFields
|
||||
|
@ -30,7 +32,7 @@ import javax.inject.Inject
|
|||
class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
|
||||
|
||||
companion object {
|
||||
const val SESSION_STORE_SCHEMA_VERSION = 7L
|
||||
const val SESSION_STORE_SCHEMA_VERSION = 8L
|
||||
}
|
||||
|
||||
override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
|
||||
|
@ -43,6 +45,7 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
|
|||
if (oldVersion <= 4) migrateTo5(realm)
|
||||
if (oldVersion <= 5) migrateTo6(realm)
|
||||
if (oldVersion <= 6) migrateTo7(realm)
|
||||
if (oldVersion <= 7) migrateTo8(realm)
|
||||
}
|
||||
|
||||
private fun migrateTo1(realm: DynamicRealm) {
|
||||
|
@ -122,4 +125,28 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
|
|||
}
|
||||
?.removeField("areAllMembersLoaded")
|
||||
}
|
||||
|
||||
private fun migrateTo8(realm: DynamicRealm) {
|
||||
Timber.d("Step 7 -> 8")
|
||||
|
||||
val editionOfEventSchema = realm.schema.create("EditionOfEvent")
|
||||
.apply {
|
||||
// setEmbedded does not return `this`...
|
||||
isEmbedded = true
|
||||
}
|
||||
.addField(EditionOfEventFields.CONTENT, String::class.java)
|
||||
.addField(EditionOfEventFields.EVENT_ID, String::class.java)
|
||||
.setRequired(EditionOfEventFields.EVENT_ID, true)
|
||||
.addField(EditionOfEventFields.SENDER_ID, String::class.java)
|
||||
.setRequired(EditionOfEventFields.SENDER_ID, true)
|
||||
.addField(EditionOfEventFields.TIMESTAMP, Long::class.java)
|
||||
.addField(EditionOfEventFields.IS_LOCAL_ECHO, Boolean::class.java)
|
||||
|
||||
realm.schema.get("EditAggregatedSummaryEntity")
|
||||
?.removeField("aggregatedContent")
|
||||
?.removeField("sourceEvents")
|
||||
?.removeField("lastEditTs")
|
||||
?.removeField("sourceLocalEchoEvents")
|
||||
?.addRealmListField(EditAggregatedSummaryEntityFields.EDITIONS.`$`, editionOfEventSchema)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,7 +97,8 @@ internal fun ChunkEntity.addTimelineEvent(roomId: String,
|
|||
this.root = eventEntity
|
||||
this.eventId = eventId
|
||||
this.roomId = roomId
|
||||
this.annotations = EventAnnotationsSummaryEntity.where(realm, eventId).findFirst()
|
||||
this.annotations = EventAnnotationsSummaryEntity.where(realm, roomId, eventId).findFirst()
|
||||
?.also { it.cleanUp(eventEntity.sender) }
|
||||
this.readReceipts = readReceiptsSummaryEntity
|
||||
this.displayIndex = displayIndex
|
||||
val roomMemberContent = roomMemberContentsByUser[senderId]
|
||||
|
|
|
@ -20,11 +20,7 @@ import org.matrix.android.sdk.api.session.room.model.EditAggregatedSummary
|
|||
import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.ReactionAggregatedSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.ReferencesAggregatedSummary
|
||||
import org.matrix.android.sdk.internal.database.model.EditAggregatedSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.ReactionAggregatedSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.ReferencesAggregatedSummaryEntity
|
||||
import io.realm.RealmList
|
||||
|
||||
internal object EventAnnotationsSummaryMapper {
|
||||
fun map(annotationsSummary: EventAnnotationsSummaryEntity): EventAnnotationsSummary {
|
||||
|
@ -40,14 +36,18 @@ internal object EventAnnotationsSummaryMapper {
|
|||
it.sourceLocalEcho.toList()
|
||||
)
|
||||
},
|
||||
editSummary = annotationsSummary.editSummary?.let {
|
||||
EditAggregatedSummary(
|
||||
ContentMapper.map(it.aggregatedContent),
|
||||
it.sourceEvents.toList(),
|
||||
it.sourceLocalEchoEvents.toList(),
|
||||
it.lastEditTs
|
||||
)
|
||||
},
|
||||
editSummary = annotationsSummary.editSummary
|
||||
?.let {
|
||||
val latestEdition = it.editions.maxByOrNull { editionOfEvent -> editionOfEvent.timestamp } ?: return@let null
|
||||
EditAggregatedSummary(
|
||||
latestContent = ContentMapper.map(latestEdition.content),
|
||||
sourceEvents = it.editions.filter { editionOfEvent -> !editionOfEvent.isLocalEcho }
|
||||
.map { editionOfEvent -> editionOfEvent.eventId },
|
||||
localEchos = it.editions.filter { editionOfEvent -> editionOfEvent.isLocalEcho }
|
||||
.map { editionOfEvent -> editionOfEvent.eventId },
|
||||
lastEditTs = latestEdition.timestamp
|
||||
)
|
||||
},
|
||||
referencesAggregatedSummary = annotationsSummary.referencesSummaryEntity?.let {
|
||||
ReferencesAggregatedSummary(
|
||||
it.eventId,
|
||||
|
@ -62,46 +62,6 @@ internal object EventAnnotationsSummaryMapper {
|
|||
|
||||
)
|
||||
}
|
||||
|
||||
fun map(annotationsSummary: EventAnnotationsSummary, roomId: String): EventAnnotationsSummaryEntity {
|
||||
val eventAnnotationsSummaryEntity = EventAnnotationsSummaryEntity()
|
||||
eventAnnotationsSummaryEntity.eventId = annotationsSummary.eventId
|
||||
eventAnnotationsSummaryEntity.roomId = roomId
|
||||
eventAnnotationsSummaryEntity.editSummary = annotationsSummary.editSummary?.let {
|
||||
EditAggregatedSummaryEntity(
|
||||
ContentMapper.map(it.aggregatedContent),
|
||||
RealmList<String>().apply { addAll(it.sourceEvents) },
|
||||
RealmList<String>().apply { addAll(it.localEchos) },
|
||||
it.lastEditTs
|
||||
)
|
||||
}
|
||||
eventAnnotationsSummaryEntity.reactionsSummary = annotationsSummary.reactionsSummary.let {
|
||||
RealmList<ReactionAggregatedSummaryEntity>().apply {
|
||||
addAll(it.map {
|
||||
ReactionAggregatedSummaryEntity(
|
||||
it.key,
|
||||
it.count,
|
||||
it.addedByMe,
|
||||
it.firstTimestamp,
|
||||
RealmList<String>().apply { addAll(it.sourceEvents) },
|
||||
RealmList<String>().apply { addAll(it.localEchoEvents) }
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
eventAnnotationsSummaryEntity.referencesSummaryEntity = annotationsSummary.referencesAggregatedSummary?.let {
|
||||
ReferencesAggregatedSummaryEntity(
|
||||
it.eventId,
|
||||
ContentMapper.map(it.content),
|
||||
RealmList<String>().apply { addAll(it.sourceEvents) },
|
||||
RealmList<String>().apply { addAll(it.localEchos) }
|
||||
)
|
||||
}
|
||||
eventAnnotationsSummaryEntity.pollResponseSummary = annotationsSummary.pollResponseSummary?.let {
|
||||
PollResponseAggregatedSummaryEntityMapper.map(it)
|
||||
}
|
||||
return eventAnnotationsSummaryEntity
|
||||
}
|
||||
}
|
||||
|
||||
internal fun EventAnnotationsSummaryEntity.asDomain(): EventAnnotationsSummary {
|
||||
|
|
|
@ -17,17 +17,24 @@ package org.matrix.android.sdk.internal.database.model
|
|||
|
||||
import io.realm.RealmList
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.RealmClass
|
||||
|
||||
/**
|
||||
* Keep the latest state of edition of a message
|
||||
* Keep all the editions of a message
|
||||
*/
|
||||
internal open class EditAggregatedSummaryEntity(
|
||||
var aggregatedContent: String? = null,
|
||||
// The list of the eventIDs used to build the summary (might be out of sync if chunked received from message chunk)
|
||||
var sourceEvents: RealmList<String> = RealmList(),
|
||||
var sourceLocalEchoEvents: RealmList<String> = RealmList(),
|
||||
var lastEditTs: Long = 0
|
||||
// The list of the editions used to build the summary (might be out of sync if chunked received from message chunk)
|
||||
var editions: RealmList<EditionOfEvent> = RealmList()
|
||||
) : RealmObject() {
|
||||
|
||||
companion object
|
||||
}
|
||||
|
||||
@RealmClass(embedded = true)
|
||||
internal open class EditionOfEvent(
|
||||
var senderId: String = "",
|
||||
var eventId: String = "",
|
||||
var content: String? = null,
|
||||
var timestamp: Long = 0,
|
||||
var isLocalEcho: Boolean = false
|
||||
) : RealmObject()
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.database.model
|
|||
import io.realm.RealmList
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.PrimaryKey
|
||||
import timber.log.Timber
|
||||
|
||||
internal open class EventAnnotationsSummaryEntity(
|
||||
@PrimaryKey
|
||||
|
@ -29,6 +30,21 @@ internal open class EventAnnotationsSummaryEntity(
|
|||
var pollResponseSummary: PollResponseAggregatedSummaryEntity? = null
|
||||
) : RealmObject() {
|
||||
|
||||
/**
|
||||
* Cleanup undesired editions, done by users different from the originalEventSender
|
||||
*/
|
||||
fun cleanUp(originalEventSenderId: String?) {
|
||||
originalEventSenderId ?: return
|
||||
|
||||
editSummary?.editions?.filter {
|
||||
it.senderId != originalEventSenderId
|
||||
}
|
||||
?.forEach {
|
||||
Timber.w("Deleting an edition from ${it.senderId} of event sent by $originalEventSenderId")
|
||||
it.deleteFromRealm()
|
||||
}
|
||||
}
|
||||
|
||||
companion object
|
||||
}
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ import io.realm.annotations.RealmModule
|
|||
EventAnnotationsSummaryEntity::class,
|
||||
ReactionAggregatedSummaryEntity::class,
|
||||
EditAggregatedSummaryEntity::class,
|
||||
EditionOfEvent::class,
|
||||
PollResponseAggregatedSummaryEntity::class,
|
||||
ReferencesAggregatedSummaryEntity::class,
|
||||
PushRulesEntity::class,
|
||||
|
|
|
@ -23,18 +23,10 @@ import io.realm.Realm
|
|||
import io.realm.RealmQuery
|
||||
import io.realm.kotlin.where
|
||||
|
||||
internal fun EventAnnotationsSummaryEntity.Companion.where(realm: Realm, eventId: String): RealmQuery<EventAnnotationsSummaryEntity> {
|
||||
val query = realm.where<EventAnnotationsSummaryEntity>()
|
||||
query.equalTo(EventAnnotationsSummaryEntityFields.EVENT_ID, eventId)
|
||||
return query
|
||||
}
|
||||
|
||||
internal fun EventAnnotationsSummaryEntity.Companion.whereInRoom(realm: Realm, roomId: String?): RealmQuery<EventAnnotationsSummaryEntity> {
|
||||
val query = realm.where<EventAnnotationsSummaryEntity>()
|
||||
if (roomId != null) {
|
||||
query.equalTo(EventAnnotationsSummaryEntityFields.ROOM_ID, roomId)
|
||||
}
|
||||
return query
|
||||
internal fun EventAnnotationsSummaryEntity.Companion.where(realm: Realm, roomId: String, eventId: String): RealmQuery<EventAnnotationsSummaryEntity> {
|
||||
return realm.where<EventAnnotationsSummaryEntity>()
|
||||
.equalTo(EventAnnotationsSummaryEntityFields.ROOM_ID, roomId)
|
||||
.equalTo(EventAnnotationsSummaryEntityFields.EVENT_ID, eventId)
|
||||
}
|
||||
|
||||
internal fun EventAnnotationsSummaryEntity.Companion.create(realm: Realm, roomId: String, eventId: String): EventAnnotationsSummaryEntity {
|
||||
|
@ -49,6 +41,6 @@ internal fun EventAnnotationsSummaryEntity.Companion.create(realm: Realm, roomId
|
|||
}
|
||||
|
||||
internal fun EventAnnotationsSummaryEntity.Companion.getOrCreate(realm: Realm, roomId: String, eventId: String): EventAnnotationsSummaryEntity {
|
||||
return EventAnnotationsSummaryEntity.where(realm, eventId).findFirst()
|
||||
?: EventAnnotationsSummaryEntity.create(realm, roomId, eventId).apply { this.roomId = roomId }
|
||||
return EventAnnotationsSummaryEntity.where(realm, roomId, eventId).findFirst()
|
||||
?: EventAnnotationsSummaryEntity.create(realm, roomId, eventId)
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package org.matrix.android.sdk.internal.session.room
|
||||
|
||||
import io.realm.Realm
|
||||
import org.matrix.android.sdk.api.crypto.VerificationState
|
||||
import org.matrix.android.sdk.api.session.events.model.AggregatedAnnotation
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
|
@ -31,9 +32,11 @@ import org.matrix.android.sdk.api.session.room.model.message.MessagePollResponse
|
|||
import org.matrix.android.sdk.api.session.room.model.message.MessageRelationContent
|
||||
import org.matrix.android.sdk.api.session.room.model.relation.ReactionContent
|
||||
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
|
||||
import org.matrix.android.sdk.internal.crypto.verification.toState
|
||||
import org.matrix.android.sdk.internal.database.mapper.ContentMapper
|
||||
import org.matrix.android.sdk.internal.database.mapper.EventMapper
|
||||
import org.matrix.android.sdk.internal.database.model.EditAggregatedSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.EditionOfEvent
|
||||
import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.EventEntity
|
||||
import org.matrix.android.sdk.internal.database.model.EventInsertType
|
||||
|
@ -50,33 +53,6 @@ import org.matrix.android.sdk.internal.session.EventInsertLiveProcessor
|
|||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
enum class VerificationState {
|
||||
REQUEST,
|
||||
WAITING,
|
||||
CANCELED_BY_ME,
|
||||
CANCELED_BY_OTHER,
|
||||
DONE
|
||||
}
|
||||
|
||||
fun VerificationState.isCanceled(): Boolean {
|
||||
return this == VerificationState.CANCELED_BY_ME || this == VerificationState.CANCELED_BY_OTHER
|
||||
}
|
||||
|
||||
// State transition with control
|
||||
private fun VerificationState?.toState(newState: VerificationState): VerificationState {
|
||||
// Cancel is always prioritary ?
|
||||
// Eg id i found that mac or keys mismatch and send a cancel and the other send a done, i have to
|
||||
// consider as canceled
|
||||
if (newState.isCanceled()) {
|
||||
return newState
|
||||
}
|
||||
// never move out of cancel
|
||||
if (this?.isCanceled() == true) {
|
||||
return this
|
||||
}
|
||||
return newState
|
||||
}
|
||||
|
||||
internal class EventRelationsAggregationProcessor @Inject constructor(@UserId private val userId: String)
|
||||
: EventInsertLiveProcessor {
|
||||
|
||||
|
@ -118,13 +94,11 @@ internal class EventRelationsAggregationProcessor @Inject constructor(@UserId pr
|
|||
Timber.v("###REACTION Agreggation in room $roomId for event ${event.eventId}")
|
||||
handleInitialAggregatedRelations(event, roomId, event.unsignedData.relations.annotations, realm)
|
||||
|
||||
EventAnnotationsSummaryEntity.where(realm, event.eventId
|
||||
?: "").findFirst()?.let {
|
||||
TimelineEventEntity.where(realm, roomId = roomId, eventId = event.eventId
|
||||
?: "").findFirst()?.let { tet ->
|
||||
tet.annotations = it
|
||||
}
|
||||
}
|
||||
EventAnnotationsSummaryEntity.where(realm, roomId, event.eventId ?: "").findFirst()
|
||||
?.let {
|
||||
TimelineEventEntity.where(realm, roomId = roomId, eventId = event.eventId ?: "").findFirst()
|
||||
?.let { tet -> tet.annotations = it }
|
||||
}
|
||||
}
|
||||
|
||||
val content: MessageContent? = event.content.toModel()
|
||||
|
@ -216,63 +190,78 @@ internal class EventRelationsAggregationProcessor @Inject constructor(@UserId pr
|
|||
// OPT OUT serer aggregation until API mature enough
|
||||
private val SHOULD_HANDLE_SERVER_AGREGGATION = false
|
||||
|
||||
private fun handleReplace(realm: Realm, event: Event, content: MessageContent, roomId: String, isLocalEcho: Boolean, relatedEventId: String? = null) {
|
||||
private fun handleReplace(realm: Realm,
|
||||
event: Event,
|
||||
content: MessageContent,
|
||||
roomId: String,
|
||||
isLocalEcho: Boolean,
|
||||
relatedEventId: String? = null) {
|
||||
val eventId = event.eventId ?: return
|
||||
val targetEventId = relatedEventId ?: content.relatesTo?.eventId ?: return
|
||||
val newContent = content.newContent ?: return
|
||||
|
||||
// Check that the sender is the same
|
||||
val editedEvent = EventEntity.where(realm, targetEventId).findFirst()
|
||||
if (editedEvent == null) {
|
||||
// We do not know yet about the edited event
|
||||
} else if (editedEvent.sender != event.senderId) {
|
||||
// Edited by someone else, ignore
|
||||
Timber.w("Ignore edition by someone else")
|
||||
return
|
||||
}
|
||||
|
||||
// ok, this is a replace
|
||||
val existing = EventAnnotationsSummaryEntity.getOrCreate(realm, roomId, targetEventId)
|
||||
val eventAnnotationsSummaryEntity = EventAnnotationsSummaryEntity.getOrCreate(realm, roomId, targetEventId)
|
||||
|
||||
// we have it
|
||||
val existingSummary = existing.editSummary
|
||||
val existingSummary = eventAnnotationsSummaryEntity.editSummary
|
||||
if (existingSummary == null) {
|
||||
Timber.v("###REPLACE new edit summary for $targetEventId, creating one (localEcho:$isLocalEcho)")
|
||||
// create the edit summary
|
||||
val editSummary = realm.createObject(EditAggregatedSummaryEntity::class.java)
|
||||
editSummary.aggregatedContent = ContentMapper.map(newContent)
|
||||
if (isLocalEcho) {
|
||||
editSummary.lastEditTs = 0
|
||||
editSummary.sourceLocalEchoEvents.add(eventId)
|
||||
} else {
|
||||
editSummary.lastEditTs = event.originServerTs ?: 0
|
||||
editSummary.sourceEvents.add(eventId)
|
||||
}
|
||||
|
||||
existing.editSummary = editSummary
|
||||
eventAnnotationsSummaryEntity.editSummary = realm.createObject(EditAggregatedSummaryEntity::class.java)
|
||||
.also { editSummary ->
|
||||
editSummary.editions.add(
|
||||
EditionOfEvent(
|
||||
senderId = event.senderId ?: "",
|
||||
eventId = event.eventId,
|
||||
content = ContentMapper.map(newContent),
|
||||
timestamp = if (isLocalEcho) 0 else event.originServerTs ?: 0,
|
||||
isLocalEcho = isLocalEcho
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if (existingSummary.sourceEvents.contains(eventId)) {
|
||||
if (existingSummary.editions.any { it.eventId == eventId }) {
|
||||
// ignore this event, we already know it (??)
|
||||
Timber.v("###REPLACE ignoring event for summary, it's known $eventId")
|
||||
return
|
||||
}
|
||||
val txId = event.unsignedData?.transactionId
|
||||
// is it a remote echo?
|
||||
if (!isLocalEcho && existingSummary.sourceLocalEchoEvents.contains(txId)) {
|
||||
if (!isLocalEcho && existingSummary.editions.any { it.eventId == txId }) {
|
||||
// ok it has already been managed
|
||||
Timber.v("###REPLACE Receiving remote echo of edit (edit already done)")
|
||||
existingSummary.sourceLocalEchoEvents.remove(txId)
|
||||
existingSummary.sourceEvents.add(event.eventId)
|
||||
} else if (
|
||||
isLocalEcho // do not rely on ts for local echo, take it
|
||||
|| event.originServerTs ?: 0 >= existingSummary.lastEditTs
|
||||
) {
|
||||
Timber.v("###REPLACE Computing aggregated edit summary (isLocalEcho:$isLocalEcho)")
|
||||
if (!isLocalEcho) {
|
||||
// Do not take local echo originServerTs here, could mess up ordering (keep old ts)
|
||||
existingSummary.lastEditTs = event.originServerTs ?: System.currentTimeMillis()
|
||||
}
|
||||
existingSummary.aggregatedContent = ContentMapper.map(newContent)
|
||||
if (isLocalEcho) {
|
||||
existingSummary.sourceLocalEchoEvents.add(eventId)
|
||||
} else {
|
||||
existingSummary.sourceEvents.add(eventId)
|
||||
existingSummary.editions.firstOrNull { it.eventId == txId }?.let {
|
||||
it.eventId = event.eventId
|
||||
it.timestamp = event.originServerTs ?: System.currentTimeMillis()
|
||||
it.isLocalEcho = false
|
||||
}
|
||||
} else {
|
||||
// ignore this event for the summary (back paginate)
|
||||
if (!isLocalEcho) {
|
||||
existingSummary.sourceEvents.add(eventId)
|
||||
}
|
||||
Timber.v("###REPLACE ignoring event for summary, it's to old $eventId")
|
||||
Timber.v("###REPLACE Computing aggregated edit summary (isLocalEcho:$isLocalEcho)")
|
||||
existingSummary.editions.add(
|
||||
EditionOfEvent(
|
||||
senderId = event.senderId ?: "",
|
||||
eventId = event.eventId,
|
||||
content = ContentMapper.map(newContent),
|
||||
timestamp = if (isLocalEcho) {
|
||||
System.currentTimeMillis()
|
||||
} else {
|
||||
// Do not take local echo originServerTs here, could mess up ordering (keep old ts)
|
||||
event.originServerTs ?: System.currentTimeMillis()
|
||||
},
|
||||
isLocalEcho = isLocalEcho
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -290,7 +279,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor(@UserId pr
|
|||
val eventTimestamp = event.originServerTs ?: return
|
||||
|
||||
// ok, this is a poll response
|
||||
var existing = EventAnnotationsSummaryEntity.where(realm, targetEventId).findFirst()
|
||||
var existing = EventAnnotationsSummaryEntity.where(realm, roomId, targetEventId).findFirst()
|
||||
if (existing == null) {
|
||||
Timber.v("## POLL creating new relation summary for $targetEventId")
|
||||
existing = EventAnnotationsSummaryEntity.create(realm, roomId, targetEventId)
|
||||
|
@ -370,7 +359,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor(@UserId pr
|
|||
aggregation.chunk?.forEach {
|
||||
if (it.type == EventType.REACTION) {
|
||||
val eventId = event.eventId ?: ""
|
||||
val existing = EventAnnotationsSummaryEntity.where(realm, eventId).findFirst()
|
||||
val existing = EventAnnotationsSummaryEntity.where(realm, roomId, eventId).findFirst()
|
||||
if (existing == null) {
|
||||
val eventSummary = EventAnnotationsSummaryEntity.create(realm, roomId, eventId)
|
||||
val sum = realm.createObject(ReactionAggregatedSummaryEntity::class.java)
|
||||
|
@ -454,46 +443,29 @@ internal class EventRelationsAggregationProcessor @Inject constructor(@UserId pr
|
|||
*/
|
||||
private fun handleRedactionOfReplace(redacted: EventEntity, relatedEventId: String, realm: Realm) {
|
||||
Timber.d("Handle redaction of m.replace")
|
||||
val eventSummary = EventAnnotationsSummaryEntity.where(realm, relatedEventId).findFirst()
|
||||
val eventSummary = EventAnnotationsSummaryEntity.where(realm, redacted.roomId, relatedEventId).findFirst()
|
||||
if (eventSummary == null) {
|
||||
Timber.w("Redaction of a replace targeting an unknown event $relatedEventId")
|
||||
return
|
||||
}
|
||||
val sourceEvents = eventSummary.editSummary?.sourceEvents
|
||||
val sourceToDiscard = sourceEvents?.indexOf(redacted.eventId)
|
||||
val sourceToDiscard = eventSummary.editSummary?.editions?.firstOrNull { it.eventId == redacted.eventId }
|
||||
if (sourceToDiscard == null) {
|
||||
Timber.w("Redaction of a replace that was not known in aggregation $sourceToDiscard")
|
||||
return
|
||||
}
|
||||
// Need to remove this event from the redaction list and compute new aggregation state
|
||||
sourceEvents.removeAt(sourceToDiscard)
|
||||
val previousEdit = sourceEvents.mapNotNull { EventEntity.where(realm, it).findFirst() }.sortedBy { it.originServerTs }.lastOrNull()
|
||||
if (previousEdit == null) {
|
||||
// revert to original
|
||||
eventSummary.editSummary?.deleteFromRealm()
|
||||
} else {
|
||||
// I have the last event
|
||||
ContentMapper.map(previousEdit.content)?.toModel<MessageContent>()?.newContent?.let { newContent ->
|
||||
eventSummary.editSummary?.lastEditTs = previousEdit.originServerTs
|
||||
?: System.currentTimeMillis()
|
||||
eventSummary.editSummary?.aggregatedContent = ContentMapper.map(newContent)
|
||||
} ?: run {
|
||||
Timber.e("Failed to udate edited summary")
|
||||
// TODO how to reccover that
|
||||
}
|
||||
}
|
||||
// Need to remove this event from the edition list
|
||||
sourceToDiscard.deleteFromRealm()
|
||||
}
|
||||
|
||||
fun handleReactionRedact(eventToPrune: EventEntity, realm: Realm, userId: String) {
|
||||
private fun handleReactionRedact(eventToPrune: EventEntity, realm: Realm, userId: String) {
|
||||
Timber.v("REDACTION of reaction ${eventToPrune.eventId}")
|
||||
// delete a reaction, need to update the annotation summary if any
|
||||
val reactionContent: ReactionContent = EventMapper.map(eventToPrune).content.toModel()
|
||||
?: return
|
||||
val reactionContent: ReactionContent = EventMapper.map(eventToPrune).content.toModel() ?: return
|
||||
val eventThatWasReacted = reactionContent.relatesTo?.eventId ?: return
|
||||
|
||||
val reactionKey = reactionContent.relatesTo.key
|
||||
Timber.v("REMOVE reaction for key $reactionKey")
|
||||
val summary = EventAnnotationsSummaryEntity.where(realm, eventThatWasReacted).findFirst()
|
||||
val summary = EventAnnotationsSummaryEntity.where(realm, eventToPrune.roomId, eventThatWasReacted).findFirst()
|
||||
if (summary != null) {
|
||||
summary.reactionsSummary.where()
|
||||
.equalTo(ReactionAggregatedSummaryEntityFields.KEY, reactionKey)
|
||||
|
|
|
@ -140,13 +140,8 @@ internal class DefaultRelationService @AssistedInject constructor(
|
|||
return eventSenderProcessor.postEvent(event, cryptoSessionInfoProvider.isRoomEncrypted(roomId))
|
||||
}
|
||||
|
||||
override fun fetchEditHistory(eventId: String, callback: MatrixCallback<List<Event>>) {
|
||||
val params = FetchEditHistoryTask.Params(roomId, eventId)
|
||||
fetchEditHistoryTask
|
||||
.configureWith(params) {
|
||||
this.callback = callback
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
override suspend fun fetchEditHistory(eventId: String): List<Event> {
|
||||
return fetchEditHistoryTask.execute(FetchEditHistoryTask.Params(roomId, eventId))
|
||||
}
|
||||
|
||||
override fun replyToMessage(eventReplied: TimelineEvent, replyText: CharSequence, autoMarkdown: Boolean): Cancelable? {
|
||||
|
@ -159,7 +154,7 @@ internal class DefaultRelationService @AssistedInject constructor(
|
|||
|
||||
override fun getEventAnnotationsSummary(eventId: String): EventAnnotationsSummary? {
|
||||
return monarchy.fetchCopyMap(
|
||||
{ EventAnnotationsSummaryEntity.where(it, eventId).findFirst() },
|
||||
{ EventAnnotationsSummaryEntity.where(it, roomId, eventId).findFirst() },
|
||||
{ entity, _ ->
|
||||
entity.asDomain()
|
||||
}
|
||||
|
@ -168,7 +163,7 @@ internal class DefaultRelationService @AssistedInject constructor(
|
|||
|
||||
override fun getEventAnnotationsSummaryLive(eventId: String): LiveData<Optional<EventAnnotationsSummary>> {
|
||||
val liveData = monarchy.findAllMappedWithChanges(
|
||||
{ EventAnnotationsSummaryEntity.where(it, eventId) },
|
||||
{ EventAnnotationsSummaryEntity.where(it, roomId, eventId) },
|
||||
{ it.asDomain() }
|
||||
)
|
||||
return Transformations.map(liveData) { results ->
|
||||
|
|
|
@ -49,8 +49,11 @@ internal class DefaultFetchEditHistoryTask @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
val events = response.chunks.toMutableList()
|
||||
response.originalEvent?.let { events.add(it) }
|
||||
return events
|
||||
// Filter out edition form other users, and redacted editions
|
||||
val originalSenderId = response.originalEvent?.senderId
|
||||
val events = response.chunks
|
||||
.filter { it.senderId == originalSenderId }
|
||||
.filter { !it.isRedacted() }
|
||||
return events + listOfNotNull(response.originalEvent)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,16 +45,16 @@ internal class DefaultFindReactionEventForUndoTask @Inject constructor(
|
|||
|
||||
override suspend fun execute(params: FindReactionEventForUndoTask.Params): FindReactionEventForUndoTask.Result {
|
||||
val eventId = Realm.getInstance(monarchy.realmConfiguration).use { realm ->
|
||||
getReactionToRedact(realm, params.reaction, params.eventId)?.eventId
|
||||
getReactionToRedact(realm, params)?.eventId
|
||||
}
|
||||
return FindReactionEventForUndoTask.Result(eventId)
|
||||
}
|
||||
|
||||
private fun getReactionToRedact(realm: Realm, reaction: String, eventId: String): EventEntity? {
|
||||
val summary = EventAnnotationsSummaryEntity.where(realm, eventId).findFirst() ?: return null
|
||||
private fun getReactionToRedact(realm: Realm, params: FindReactionEventForUndoTask.Params): EventEntity? {
|
||||
val summary = EventAnnotationsSummaryEntity.where(realm, params.roomId, params.eventId).findFirst() ?: return null
|
||||
|
||||
val rase = summary.reactionsSummary.where()
|
||||
.equalTo(ReactionAggregatedSummaryEntityFields.KEY, reaction)
|
||||
.equalTo(ReactionAggregatedSummaryEntityFields.KEY, params.reaction)
|
||||
.findFirst() ?: return null
|
||||
|
||||
// want to find the event originated by me!
|
||||
|
|
|
@ -47,22 +47,22 @@ internal class DefaultUpdateQuickReactionTask @Inject constructor(@SessionDataba
|
|||
override suspend fun execute(params: UpdateQuickReactionTask.Params): UpdateQuickReactionTask.Result {
|
||||
var res: Pair<String?, List<String>?>? = null
|
||||
monarchy.doWithRealm { realm ->
|
||||
res = updateQuickReaction(realm, params.reaction, params.oppositeReaction, params.eventId)
|
||||
res = updateQuickReaction(realm, params)
|
||||
}
|
||||
return UpdateQuickReactionTask.Result(res?.first, res?.second.orEmpty())
|
||||
}
|
||||
|
||||
private fun updateQuickReaction(realm: Realm, reaction: String, oppositeReaction: String, eventId: String): Pair<String?, List<String>?> {
|
||||
private fun updateQuickReaction(realm: Realm, params: UpdateQuickReactionTask.Params): Pair<String?, List<String>?> {
|
||||
// the emoji reaction has been selected, we need to check if we have reacted it or not
|
||||
val existingSummary = EventAnnotationsSummaryEntity.where(realm, eventId).findFirst()
|
||||
?: return Pair(reaction, null)
|
||||
val existingSummary = EventAnnotationsSummaryEntity.where(realm, params.roomId, params.eventId).findFirst()
|
||||
?: return Pair(params.reaction, null)
|
||||
|
||||
// Ok there is already reactions on this event, have we reacted to it
|
||||
val aggregationForReaction = existingSummary.reactionsSummary.where()
|
||||
.equalTo(ReactionAggregatedSummaryEntityFields.KEY, reaction)
|
||||
.equalTo(ReactionAggregatedSummaryEntityFields.KEY, params.reaction)
|
||||
.findFirst()
|
||||
val aggregationForOppositeReaction = existingSummary.reactionsSummary.where()
|
||||
.equalTo(ReactionAggregatedSummaryEntityFields.KEY, oppositeReaction)
|
||||
.equalTo(ReactionAggregatedSummaryEntityFields.KEY, params.oppositeReaction)
|
||||
.findFirst()
|
||||
|
||||
if (aggregationForReaction == null || !aggregationForReaction.addedByMe) {
|
||||
|
@ -72,7 +72,7 @@ internal class DefaultUpdateQuickReactionTask @Inject constructor(@SessionDataba
|
|||
val entity = EventEntity.where(realm, it).findFirst()
|
||||
if (entity?.sender == userId) entity.eventId else null
|
||||
}
|
||||
return Pair(reaction, toRedact)
|
||||
return Pair(params.reaction, toRedact)
|
||||
} else {
|
||||
// I already added it, so i need to undo it (like a toggle)
|
||||
// find all m.redaction coming from me to readact them
|
||||
|
|
|
@ -85,7 +85,6 @@ import org.matrix.android.sdk.api.session.room.model.Membership
|
|||
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageType
|
||||
import org.matrix.android.sdk.api.session.room.model.message.OptionItem
|
||||
import org.matrix.android.sdk.api.session.room.model.message.getFileUrl
|
||||
|
@ -95,6 +94,7 @@ import org.matrix.android.sdk.api.session.room.read.ReadService
|
|||
import org.matrix.android.sdk.api.session.room.send.UserDraft
|
||||
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
|
||||
import org.matrix.android.sdk.api.session.room.timeline.getRelationContent
|
||||
import org.matrix.android.sdk.api.session.room.timeline.getTextEditableContent
|
||||
import org.matrix.android.sdk.api.session.widgets.model.Widget
|
||||
|
@ -825,9 +825,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||
room.editReply(state.sendMode.timelineEvent, it, action.text.toString())
|
||||
}
|
||||
} else {
|
||||
val messageContent: MessageContent? =
|
||||
state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel()
|
||||
?: state.sendMode.timelineEvent.root.getClearContent().toModel()
|
||||
val messageContent = state.sendMode.timelineEvent.getLastMessageContent()
|
||||
val existingBody = messageContent?.body ?: ""
|
||||
if (existingBody != action.text) {
|
||||
room.editTextMessage(state.sendMode.timelineEvent.root.eventId ?: "",
|
||||
|
@ -842,9 +840,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||
popDraft()
|
||||
}
|
||||
is SendMode.QUOTE -> {
|
||||
val messageContent: MessageContent? =
|
||||
state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel()
|
||||
?: state.sendMode.timelineEvent.root.getClearContent().toModel()
|
||||
val messageContent = state.sendMode.timelineEvent.getLastMessageContent()
|
||||
val textMsg = messageContent?.body
|
||||
|
||||
val finalText = legacyRiotQuoteText(textMsg, action.text.toString())
|
||||
|
|
|
@ -229,8 +229,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
|||
}
|
||||
|
||||
private fun actionsForEvent(timelineEvent: TimelineEvent, actionPermissions: ActionPermissions): List<EventSharedAction> {
|
||||
val messageContent: MessageContent? = timelineEvent.annotations?.editSummary?.aggregatedContent.toModel()
|
||||
?: timelineEvent.root.getClearContent().toModel()
|
||||
val messageContent = timelineEvent.getLastMessageContent()
|
||||
val msgType = messageContent?.msgType
|
||||
|
||||
return arrayListOf<EventSharedAction>().apply {
|
||||
|
|
|
@ -15,42 +15,28 @@
|
|||
*/
|
||||
package im.vector.app.features.home.room.detail.timeline.edithistory
|
||||
|
||||
import com.airbnb.mvrx.Async
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.FragmentViewModelContext
|
||||
import com.airbnb.mvrx.Loading
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import im.vector.app.core.date.VectorDateFormatter
|
||||
import im.vector.app.core.platform.EmptyAction
|
||||
import im.vector.app.core.platform.EmptyViewEvents
|
||||
import im.vector.app.core.platform.VectorViewModel
|
||||
import im.vector.app.features.home.room.detail.timeline.action.TimelineEventFragmentArgs
|
||||
import org.matrix.android.sdk.api.MatrixCallback
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.isReply
|
||||
import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult
|
||||
import timber.log.Timber
|
||||
import java.util.UUID
|
||||
|
||||
data class ViewEditHistoryViewState(
|
||||
val eventId: String,
|
||||
val roomId: String,
|
||||
val isOriginalAReply: Boolean = false,
|
||||
val editList: Async<List<Event>> = Uninitialized)
|
||||
: MvRxState {
|
||||
|
||||
constructor(args: TimelineEventFragmentArgs) : this(roomId = args.roomId, eventId = args.eventId)
|
||||
}
|
||||
|
||||
class ViewEditHistoryViewModel @AssistedInject constructor(@Assisted
|
||||
initialState: ViewEditHistoryViewState,
|
||||
val session: Session,
|
||||
|
@ -82,48 +68,48 @@ class ViewEditHistoryViewModel @AssistedInject constructor(@Assisted
|
|||
|
||||
private fun loadHistory() {
|
||||
setState { copy(editList = Loading()) }
|
||||
room.fetchEditHistory(eventId, object : MatrixCallback<List<Event>> {
|
||||
override fun onFailure(failure: Throwable) {
|
||||
|
||||
viewModelScope.launch {
|
||||
val data = try {
|
||||
room.fetchEditHistory(eventId)
|
||||
} catch (failure: Throwable) {
|
||||
setState {
|
||||
copy(editList = Fail(failure))
|
||||
}
|
||||
return@launch
|
||||
}
|
||||
|
||||
override fun onSuccess(data: List<Event>) {
|
||||
var originalIsReply = false
|
||||
var originalIsReply = false
|
||||
|
||||
val events = data.map { event ->
|
||||
val timelineID = event.roomId + UUID.randomUUID().toString()
|
||||
event.also {
|
||||
// We need to check encryption
|
||||
if (it.isEncrypted() && it.mxDecryptionResult == null) {
|
||||
// for now decrypt sync
|
||||
try {
|
||||
val result = session.cryptoService().decryptEvent(it, timelineID)
|
||||
it.mxDecryptionResult = OlmDecryptionResult(
|
||||
payload = result.clearEvent,
|
||||
senderKey = result.senderCurve25519Key,
|
||||
keysClaimed = result.claimedEd25519Key?.let { k -> mapOf("ed25519" to k) },
|
||||
forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain
|
||||
)
|
||||
} catch (e: MXCryptoError) {
|
||||
Timber.w("Failed to decrypt event in history")
|
||||
}
|
||||
}
|
||||
|
||||
if (event.eventId == it.eventId) {
|
||||
originalIsReply = it.isReply()
|
||||
}
|
||||
data.forEach { event ->
|
||||
val timelineID = event.roomId + UUID.randomUUID().toString()
|
||||
// We need to check encryption
|
||||
if (event.isEncrypted() && event.mxDecryptionResult == null) {
|
||||
// for now decrypt sync
|
||||
try {
|
||||
val result = session.cryptoService().decryptEvent(event, timelineID)
|
||||
event.mxDecryptionResult = OlmDecryptionResult(
|
||||
payload = result.clearEvent,
|
||||
senderKey = result.senderCurve25519Key,
|
||||
keysClaimed = result.claimedEd25519Key?.let { k -> mapOf("ed25519" to k) },
|
||||
forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain
|
||||
)
|
||||
} catch (e: MXCryptoError) {
|
||||
Timber.w("Failed to decrypt event in history")
|
||||
}
|
||||
}
|
||||
setState {
|
||||
copy(
|
||||
editList = Success(events),
|
||||
isOriginalAReply = originalIsReply
|
||||
)
|
||||
|
||||
if (event.eventId == eventId) {
|
||||
originalIsReply = event.isReply()
|
||||
}
|
||||
}
|
||||
})
|
||||
setState {
|
||||
copy(
|
||||
editList = Success(data),
|
||||
isOriginalAReply = originalIsReply
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun handle(action: EmptyAction) {
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 2021 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.app.features.home.room.detail.timeline.edithistory
|
||||
|
||||
import com.airbnb.mvrx.Async
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import im.vector.app.features.home.room.detail.timeline.action.TimelineEventFragmentArgs
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
|
||||
data class ViewEditHistoryViewState(
|
||||
val eventId: String,
|
||||
val roomId: String,
|
||||
val isOriginalAReply: Boolean = false,
|
||||
val editList: Async<List<Event>> = Uninitialized)
|
||||
: MvRxState {
|
||||
|
||||
constructor(args: TimelineEventFragmentArgs) : this(roomId = args.roomId, eventId = args.eventId)
|
||||
}
|
|
@ -26,6 +26,7 @@ import im.vector.app.features.home.room.detail.timeline.helper.MessageInformatio
|
|||
import im.vector.app.features.home.room.detail.timeline.helper.MessageItemAttributesFactory
|
||||
import im.vector.app.features.home.room.detail.timeline.item.StatusTileTimelineItem
|
||||
import im.vector.app.features.home.room.detail.timeline.item.StatusTileTimelineItem_
|
||||
import org.matrix.android.sdk.api.crypto.VerificationState
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.CancelCode
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.safeValueOf
|
||||
|
@ -35,7 +36,6 @@ import org.matrix.android.sdk.api.session.events.model.toModel
|
|||
import org.matrix.android.sdk.api.session.room.model.message.MessageRelationContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationCancelContent
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
import org.matrix.android.sdk.internal.session.room.VerificationState
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
|
|
|
@ -26,6 +26,7 @@ import im.vector.app.features.home.room.detail.timeline.item.ReactionInfoData
|
|||
import im.vector.app.features.home.room.detail.timeline.item.ReadReceiptData
|
||||
import im.vector.app.features.home.room.detail.timeline.item.ReferencesInfoData
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
import org.matrix.android.sdk.api.crypto.VerificationState
|
||||
import org.matrix.android.sdk.api.extensions.orFalse
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
|
@ -37,7 +38,6 @@ import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
|||
import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
|
||||
import org.matrix.android.sdk.api.session.room.timeline.hasBeenEdited
|
||||
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
|
||||
import org.matrix.android.sdk.internal.session.room.VerificationState
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
|
|
|
@ -18,9 +18,9 @@ package im.vector.app.features.home.room.detail.timeline.item
|
|||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import org.matrix.android.sdk.api.crypto.VerificationState
|
||||
import org.matrix.android.sdk.api.session.room.send.SendState
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
import org.matrix.android.sdk.internal.session.room.VerificationState
|
||||
|
||||
@Parcelize
|
||||
data class MessageInformationData(
|
||||
|
|
|
@ -35,8 +35,8 @@ import im.vector.app.features.home.AvatarRenderer
|
|||
import im.vector.app.features.home.room.detail.RoomDetailAction
|
||||
import im.vector.app.features.home.room.detail.timeline.MessageColorProvider
|
||||
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
|
||||
import org.matrix.android.sdk.api.crypto.VerificationState
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
|
||||
import org.matrix.android.sdk.internal.session.room.VerificationState
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_timeline_event_base_state)
|
||||
abstract class VerificationRequestItem : AbsBaseMessageItem<VerificationRequestItem.Holder>() {
|
||||
|
|
|
@ -48,6 +48,7 @@ class HomeserverSettingsController @Inject constructor(
|
|||
data ?: return
|
||||
|
||||
buildHeader(data)
|
||||
buildCapabilities(data)
|
||||
when (val federationVersion = data.federationVersion) {
|
||||
is Loading,
|
||||
is Uninitialized ->
|
||||
|
@ -63,7 +64,6 @@ class HomeserverSettingsController @Inject constructor(
|
|||
is Success ->
|
||||
buildFederationVersion(federationVersion())
|
||||
}
|
||||
buildCapabilities(data)
|
||||
}
|
||||
|
||||
private fun buildHeader(state: HomeServerSettingsViewState) {
|
||||
|
|
Loading…
Add table
Reference in a new issue