mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-03-25 23:39:00 +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
CHANGES.md
attachment-viewer/src/main/res/layout
matrix-sdk-android/src/main/java/org/matrix/android/sdk
api
crypto
session/room
internal
vector/src/main/java/im/vector/app/features
|
@ -30,7 +30,7 @@ Test:
|
||||||
-
|
-
|
||||||
|
|
||||||
Other changes:
|
Other changes:
|
||||||
-
|
- Rework edition of event management
|
||||||
|
|
||||||
Changes in Element 1.1.0 (2021-02-19)
|
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
|
import org.matrix.android.sdk.api.session.events.model.Content
|
||||||
|
|
||||||
data class EditAggregatedSummary(
|
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)
|
// 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 sourceEvents: List<String>,
|
||||||
val localEchos: 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.Json
|
||||||
import com.squareup.moshi.JsonClass
|
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.
|
* Contains an aggregated summary info of the references.
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
package org.matrix.android.sdk.api.session.room.model.relation
|
package org.matrix.android.sdk.api.session.room.model.relation
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
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.events.model.Event
|
||||||
import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary
|
import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
|
@ -92,8 +91,11 @@ interface RelationService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the edit history of the given event
|
* 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)
|
* 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) {
|
return if (root.getClearType() == EventType.STICKER) {
|
||||||
root.getClearContent().toModel<MessageStickerContent>()
|
root.getClearContent().toModel<MessageStickerContent>()
|
||||||
} else {
|
} else {
|
||||||
annotations?.editSummary?.aggregatedContent?.toModel()
|
(annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel()
|
||||||
?: 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.DynamicRealm
|
||||||
import io.realm.RealmMigration
|
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.HomeServerCapabilitiesEntityFields
|
||||||
import org.matrix.android.sdk.internal.database.model.PendingThreePidEntityFields
|
import org.matrix.android.sdk.internal.database.model.PendingThreePidEntityFields
|
||||||
import org.matrix.android.sdk.internal.database.model.PreviewUrlCacheEntityFields
|
import org.matrix.android.sdk.internal.database.model.PreviewUrlCacheEntityFields
|
||||||
|
@ -30,7 +32,7 @@ import javax.inject.Inject
|
||||||
class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
|
class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
|
||||||
|
|
||||||
companion object {
|
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) {
|
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 <= 4) migrateTo5(realm)
|
||||||
if (oldVersion <= 5) migrateTo6(realm)
|
if (oldVersion <= 5) migrateTo6(realm)
|
||||||
if (oldVersion <= 6) migrateTo7(realm)
|
if (oldVersion <= 6) migrateTo7(realm)
|
||||||
|
if (oldVersion <= 7) migrateTo8(realm)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun migrateTo1(realm: DynamicRealm) {
|
private fun migrateTo1(realm: DynamicRealm) {
|
||||||
|
@ -122,4 +125,28 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
|
||||||
}
|
}
|
||||||
?.removeField("areAllMembersLoaded")
|
?.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.root = eventEntity
|
||||||
this.eventId = eventId
|
this.eventId = eventId
|
||||||
this.roomId = roomId
|
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.readReceipts = readReceiptsSummaryEntity
|
||||||
this.displayIndex = displayIndex
|
this.displayIndex = displayIndex
|
||||||
val roomMemberContent = roomMemberContentsByUser[senderId]
|
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.EventAnnotationsSummary
|
||||||
import org.matrix.android.sdk.api.session.room.model.ReactionAggregatedSummary
|
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.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.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 {
|
internal object EventAnnotationsSummaryMapper {
|
||||||
fun map(annotationsSummary: EventAnnotationsSummaryEntity): EventAnnotationsSummary {
|
fun map(annotationsSummary: EventAnnotationsSummaryEntity): EventAnnotationsSummary {
|
||||||
|
@ -40,14 +36,18 @@ internal object EventAnnotationsSummaryMapper {
|
||||||
it.sourceLocalEcho.toList()
|
it.sourceLocalEcho.toList()
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
editSummary = annotationsSummary.editSummary?.let {
|
editSummary = annotationsSummary.editSummary
|
||||||
EditAggregatedSummary(
|
?.let {
|
||||||
ContentMapper.map(it.aggregatedContent),
|
val latestEdition = it.editions.maxByOrNull { editionOfEvent -> editionOfEvent.timestamp } ?: return@let null
|
||||||
it.sourceEvents.toList(),
|
EditAggregatedSummary(
|
||||||
it.sourceLocalEchoEvents.toList(),
|
latestContent = ContentMapper.map(latestEdition.content),
|
||||||
it.lastEditTs
|
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 = annotationsSummary.referencesSummaryEntity?.let {
|
||||||
ReferencesAggregatedSummary(
|
ReferencesAggregatedSummary(
|
||||||
it.eventId,
|
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 {
|
internal fun EventAnnotationsSummaryEntity.asDomain(): EventAnnotationsSummary {
|
||||||
|
|
|
@ -17,17 +17,24 @@ package org.matrix.android.sdk.internal.database.model
|
||||||
|
|
||||||
import io.realm.RealmList
|
import io.realm.RealmList
|
||||||
import io.realm.RealmObject
|
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(
|
internal open class EditAggregatedSummaryEntity(
|
||||||
var aggregatedContent: String? = null,
|
// The list of the editions used to build the summary (might be out of sync if chunked received from message chunk)
|
||||||
// The list of the eventIDs used to build the summary (might be out of sync if chunked received from message chunk)
|
var editions: RealmList<EditionOfEvent> = RealmList()
|
||||||
var sourceEvents: RealmList<String> = RealmList(),
|
|
||||||
var sourceLocalEchoEvents: RealmList<String> = RealmList(),
|
|
||||||
var lastEditTs: Long = 0
|
|
||||||
) : RealmObject() {
|
) : RealmObject() {
|
||||||
|
|
||||||
companion object
|
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.RealmList
|
||||||
import io.realm.RealmObject
|
import io.realm.RealmObject
|
||||||
import io.realm.annotations.PrimaryKey
|
import io.realm.annotations.PrimaryKey
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
internal open class EventAnnotationsSummaryEntity(
|
internal open class EventAnnotationsSummaryEntity(
|
||||||
@PrimaryKey
|
@PrimaryKey
|
||||||
|
@ -29,6 +30,21 @@ internal open class EventAnnotationsSummaryEntity(
|
||||||
var pollResponseSummary: PollResponseAggregatedSummaryEntity? = null
|
var pollResponseSummary: PollResponseAggregatedSummaryEntity? = null
|
||||||
) : RealmObject() {
|
) : 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
|
companion object
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@ import io.realm.annotations.RealmModule
|
||||||
EventAnnotationsSummaryEntity::class,
|
EventAnnotationsSummaryEntity::class,
|
||||||
ReactionAggregatedSummaryEntity::class,
|
ReactionAggregatedSummaryEntity::class,
|
||||||
EditAggregatedSummaryEntity::class,
|
EditAggregatedSummaryEntity::class,
|
||||||
|
EditionOfEvent::class,
|
||||||
PollResponseAggregatedSummaryEntity::class,
|
PollResponseAggregatedSummaryEntity::class,
|
||||||
ReferencesAggregatedSummaryEntity::class,
|
ReferencesAggregatedSummaryEntity::class,
|
||||||
PushRulesEntity::class,
|
PushRulesEntity::class,
|
||||||
|
|
|
@ -23,18 +23,10 @@ import io.realm.Realm
|
||||||
import io.realm.RealmQuery
|
import io.realm.RealmQuery
|
||||||
import io.realm.kotlin.where
|
import io.realm.kotlin.where
|
||||||
|
|
||||||
internal fun EventAnnotationsSummaryEntity.Companion.where(realm: Realm, eventId: String): RealmQuery<EventAnnotationsSummaryEntity> {
|
internal fun EventAnnotationsSummaryEntity.Companion.where(realm: Realm, roomId: String, eventId: String): RealmQuery<EventAnnotationsSummaryEntity> {
|
||||||
val query = realm.where<EventAnnotationsSummaryEntity>()
|
return realm.where<EventAnnotationsSummaryEntity>()
|
||||||
query.equalTo(EventAnnotationsSummaryEntityFields.EVENT_ID, eventId)
|
.equalTo(EventAnnotationsSummaryEntityFields.ROOM_ID, roomId)
|
||||||
return query
|
.equalTo(EventAnnotationsSummaryEntityFields.EVENT_ID, eventId)
|
||||||
}
|
|
||||||
|
|
||||||
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.create(realm: Realm, roomId: String, eventId: String): EventAnnotationsSummaryEntity {
|
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 {
|
internal fun EventAnnotationsSummaryEntity.Companion.getOrCreate(realm: Realm, roomId: String, eventId: String): EventAnnotationsSummaryEntity {
|
||||||
return EventAnnotationsSummaryEntity.where(realm, eventId).findFirst()
|
return EventAnnotationsSummaryEntity.where(realm, roomId, eventId).findFirst()
|
||||||
?: EventAnnotationsSummaryEntity.create(realm, roomId, eventId).apply { this.roomId = roomId }
|
?: EventAnnotationsSummaryEntity.create(realm, roomId, eventId)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package org.matrix.android.sdk.internal.session.room
|
package org.matrix.android.sdk.internal.session.room
|
||||||
|
|
||||||
import io.realm.Realm
|
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.AggregatedAnnotation
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
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.message.MessageRelationContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.relation.ReactionContent
|
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.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.ContentMapper
|
||||||
import org.matrix.android.sdk.internal.database.mapper.EventMapper
|
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.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.EventAnnotationsSummaryEntity
|
||||||
import org.matrix.android.sdk.internal.database.model.EventEntity
|
import org.matrix.android.sdk.internal.database.model.EventEntity
|
||||||
import org.matrix.android.sdk.internal.database.model.EventInsertType
|
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 timber.log.Timber
|
||||||
import javax.inject.Inject
|
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)
|
internal class EventRelationsAggregationProcessor @Inject constructor(@UserId private val userId: String)
|
||||||
: EventInsertLiveProcessor {
|
: EventInsertLiveProcessor {
|
||||||
|
|
||||||
|
@ -118,13 +94,11 @@ internal class EventRelationsAggregationProcessor @Inject constructor(@UserId pr
|
||||||
Timber.v("###REACTION Agreggation in room $roomId for event ${event.eventId}")
|
Timber.v("###REACTION Agreggation in room $roomId for event ${event.eventId}")
|
||||||
handleInitialAggregatedRelations(event, roomId, event.unsignedData.relations.annotations, realm)
|
handleInitialAggregatedRelations(event, roomId, event.unsignedData.relations.annotations, realm)
|
||||||
|
|
||||||
EventAnnotationsSummaryEntity.where(realm, event.eventId
|
EventAnnotationsSummaryEntity.where(realm, roomId, event.eventId ?: "").findFirst()
|
||||||
?: "").findFirst()?.let {
|
?.let {
|
||||||
TimelineEventEntity.where(realm, roomId = roomId, eventId = event.eventId
|
TimelineEventEntity.where(realm, roomId = roomId, eventId = event.eventId ?: "").findFirst()
|
||||||
?: "").findFirst()?.let { tet ->
|
?.let { tet -> tet.annotations = it }
|
||||||
tet.annotations = it
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val content: MessageContent? = event.content.toModel()
|
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
|
// OPT OUT serer aggregation until API mature enough
|
||||||
private val SHOULD_HANDLE_SERVER_AGREGGATION = false
|
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 eventId = event.eventId ?: return
|
||||||
val targetEventId = relatedEventId ?: content.relatesTo?.eventId ?: return
|
val targetEventId = relatedEventId ?: content.relatesTo?.eventId ?: return
|
||||||
val newContent = content.newContent ?: 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
|
// ok, this is a replace
|
||||||
val existing = EventAnnotationsSummaryEntity.getOrCreate(realm, roomId, targetEventId)
|
val eventAnnotationsSummaryEntity = EventAnnotationsSummaryEntity.getOrCreate(realm, roomId, targetEventId)
|
||||||
|
|
||||||
// we have it
|
// we have it
|
||||||
val existingSummary = existing.editSummary
|
val existingSummary = eventAnnotationsSummaryEntity.editSummary
|
||||||
if (existingSummary == null) {
|
if (existingSummary == null) {
|
||||||
Timber.v("###REPLACE new edit summary for $targetEventId, creating one (localEcho:$isLocalEcho)")
|
Timber.v("###REPLACE new edit summary for $targetEventId, creating one (localEcho:$isLocalEcho)")
|
||||||
// create the edit summary
|
// create the edit summary
|
||||||
val editSummary = realm.createObject(EditAggregatedSummaryEntity::class.java)
|
eventAnnotationsSummaryEntity.editSummary = realm.createObject(EditAggregatedSummaryEntity::class.java)
|
||||||
editSummary.aggregatedContent = ContentMapper.map(newContent)
|
.also { editSummary ->
|
||||||
if (isLocalEcho) {
|
editSummary.editions.add(
|
||||||
editSummary.lastEditTs = 0
|
EditionOfEvent(
|
||||||
editSummary.sourceLocalEchoEvents.add(eventId)
|
senderId = event.senderId ?: "",
|
||||||
} else {
|
eventId = event.eventId,
|
||||||
editSummary.lastEditTs = event.originServerTs ?: 0
|
content = ContentMapper.map(newContent),
|
||||||
editSummary.sourceEvents.add(eventId)
|
timestamp = if (isLocalEcho) 0 else event.originServerTs ?: 0,
|
||||||
}
|
isLocalEcho = isLocalEcho
|
||||||
|
)
|
||||||
existing.editSummary = editSummary
|
)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (existingSummary.sourceEvents.contains(eventId)) {
|
if (existingSummary.editions.any { it.eventId == eventId }) {
|
||||||
// ignore this event, we already know it (??)
|
// ignore this event, we already know it (??)
|
||||||
Timber.v("###REPLACE ignoring event for summary, it's known $eventId")
|
Timber.v("###REPLACE ignoring event for summary, it's known $eventId")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val txId = event.unsignedData?.transactionId
|
val txId = event.unsignedData?.transactionId
|
||||||
// is it a remote echo?
|
// 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
|
// ok it has already been managed
|
||||||
Timber.v("###REPLACE Receiving remote echo of edit (edit already done)")
|
Timber.v("###REPLACE Receiving remote echo of edit (edit already done)")
|
||||||
existingSummary.sourceLocalEchoEvents.remove(txId)
|
existingSummary.editions.firstOrNull { it.eventId == txId }?.let {
|
||||||
existingSummary.sourceEvents.add(event.eventId)
|
it.eventId = event.eventId
|
||||||
} else if (
|
it.timestamp = event.originServerTs ?: System.currentTimeMillis()
|
||||||
isLocalEcho // do not rely on ts for local echo, take it
|
it.isLocalEcho = false
|
||||||
|| 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)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// ignore this event for the summary (back paginate)
|
Timber.v("###REPLACE Computing aggregated edit summary (isLocalEcho:$isLocalEcho)")
|
||||||
if (!isLocalEcho) {
|
existingSummary.editions.add(
|
||||||
existingSummary.sourceEvents.add(eventId)
|
EditionOfEvent(
|
||||||
}
|
senderId = event.senderId ?: "",
|
||||||
Timber.v("###REPLACE ignoring event for summary, it's to old $eventId")
|
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
|
val eventTimestamp = event.originServerTs ?: return
|
||||||
|
|
||||||
// ok, this is a poll response
|
// ok, this is a poll response
|
||||||
var existing = EventAnnotationsSummaryEntity.where(realm, targetEventId).findFirst()
|
var existing = EventAnnotationsSummaryEntity.where(realm, roomId, targetEventId).findFirst()
|
||||||
if (existing == null) {
|
if (existing == null) {
|
||||||
Timber.v("## POLL creating new relation summary for $targetEventId")
|
Timber.v("## POLL creating new relation summary for $targetEventId")
|
||||||
existing = EventAnnotationsSummaryEntity.create(realm, roomId, targetEventId)
|
existing = EventAnnotationsSummaryEntity.create(realm, roomId, targetEventId)
|
||||||
|
@ -370,7 +359,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor(@UserId pr
|
||||||
aggregation.chunk?.forEach {
|
aggregation.chunk?.forEach {
|
||||||
if (it.type == EventType.REACTION) {
|
if (it.type == EventType.REACTION) {
|
||||||
val eventId = event.eventId ?: ""
|
val eventId = event.eventId ?: ""
|
||||||
val existing = EventAnnotationsSummaryEntity.where(realm, eventId).findFirst()
|
val existing = EventAnnotationsSummaryEntity.where(realm, roomId, eventId).findFirst()
|
||||||
if (existing == null) {
|
if (existing == null) {
|
||||||
val eventSummary = EventAnnotationsSummaryEntity.create(realm, roomId, eventId)
|
val eventSummary = EventAnnotationsSummaryEntity.create(realm, roomId, eventId)
|
||||||
val sum = realm.createObject(ReactionAggregatedSummaryEntity::class.java)
|
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) {
|
private fun handleRedactionOfReplace(redacted: EventEntity, relatedEventId: String, realm: Realm) {
|
||||||
Timber.d("Handle redaction of m.replace")
|
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) {
|
if (eventSummary == null) {
|
||||||
Timber.w("Redaction of a replace targeting an unknown event $relatedEventId")
|
Timber.w("Redaction of a replace targeting an unknown event $relatedEventId")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val sourceEvents = eventSummary.editSummary?.sourceEvents
|
val sourceToDiscard = eventSummary.editSummary?.editions?.firstOrNull { it.eventId == redacted.eventId }
|
||||||
val sourceToDiscard = sourceEvents?.indexOf(redacted.eventId)
|
|
||||||
if (sourceToDiscard == null) {
|
if (sourceToDiscard == null) {
|
||||||
Timber.w("Redaction of a replace that was not known in aggregation $sourceToDiscard")
|
Timber.w("Redaction of a replace that was not known in aggregation $sourceToDiscard")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Need to remove this event from the redaction list and compute new aggregation state
|
// Need to remove this event from the edition list
|
||||||
sourceEvents.removeAt(sourceToDiscard)
|
sourceToDiscard.deleteFromRealm()
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handleReactionRedact(eventToPrune: EventEntity, realm: Realm, userId: String) {
|
private fun handleReactionRedact(eventToPrune: EventEntity, realm: Realm, userId: String) {
|
||||||
Timber.v("REDACTION of reaction ${eventToPrune.eventId}")
|
Timber.v("REDACTION of reaction ${eventToPrune.eventId}")
|
||||||
// delete a reaction, need to update the annotation summary if any
|
// delete a reaction, need to update the annotation summary if any
|
||||||
val reactionContent: ReactionContent = EventMapper.map(eventToPrune).content.toModel()
|
val reactionContent: ReactionContent = EventMapper.map(eventToPrune).content.toModel() ?: return
|
||||||
?: return
|
|
||||||
val eventThatWasReacted = reactionContent.relatesTo?.eventId ?: return
|
val eventThatWasReacted = reactionContent.relatesTo?.eventId ?: return
|
||||||
|
|
||||||
val reactionKey = reactionContent.relatesTo.key
|
val reactionKey = reactionContent.relatesTo.key
|
||||||
Timber.v("REMOVE reaction for key $reactionKey")
|
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) {
|
if (summary != null) {
|
||||||
summary.reactionsSummary.where()
|
summary.reactionsSummary.where()
|
||||||
.equalTo(ReactionAggregatedSummaryEntityFields.KEY, reactionKey)
|
.equalTo(ReactionAggregatedSummaryEntityFields.KEY, reactionKey)
|
||||||
|
|
|
@ -140,13 +140,8 @@ internal class DefaultRelationService @AssistedInject constructor(
|
||||||
return eventSenderProcessor.postEvent(event, cryptoSessionInfoProvider.isRoomEncrypted(roomId))
|
return eventSenderProcessor.postEvent(event, cryptoSessionInfoProvider.isRoomEncrypted(roomId))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun fetchEditHistory(eventId: String, callback: MatrixCallback<List<Event>>) {
|
override suspend fun fetchEditHistory(eventId: String): List<Event> {
|
||||||
val params = FetchEditHistoryTask.Params(roomId, eventId)
|
return fetchEditHistoryTask.execute(FetchEditHistoryTask.Params(roomId, eventId))
|
||||||
fetchEditHistoryTask
|
|
||||||
.configureWith(params) {
|
|
||||||
this.callback = callback
|
|
||||||
}
|
|
||||||
.executeBy(taskExecutor)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun replyToMessage(eventReplied: TimelineEvent, replyText: CharSequence, autoMarkdown: Boolean): Cancelable? {
|
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? {
|
override fun getEventAnnotationsSummary(eventId: String): EventAnnotationsSummary? {
|
||||||
return monarchy.fetchCopyMap(
|
return monarchy.fetchCopyMap(
|
||||||
{ EventAnnotationsSummaryEntity.where(it, eventId).findFirst() },
|
{ EventAnnotationsSummaryEntity.where(it, roomId, eventId).findFirst() },
|
||||||
{ entity, _ ->
|
{ entity, _ ->
|
||||||
entity.asDomain()
|
entity.asDomain()
|
||||||
}
|
}
|
||||||
|
@ -168,7 +163,7 @@ internal class DefaultRelationService @AssistedInject constructor(
|
||||||
|
|
||||||
override fun getEventAnnotationsSummaryLive(eventId: String): LiveData<Optional<EventAnnotationsSummary>> {
|
override fun getEventAnnotationsSummaryLive(eventId: String): LiveData<Optional<EventAnnotationsSummary>> {
|
||||||
val liveData = monarchy.findAllMappedWithChanges(
|
val liveData = monarchy.findAllMappedWithChanges(
|
||||||
{ EventAnnotationsSummaryEntity.where(it, eventId) },
|
{ EventAnnotationsSummaryEntity.where(it, roomId, eventId) },
|
||||||
{ it.asDomain() }
|
{ it.asDomain() }
|
||||||
)
|
)
|
||||||
return Transformations.map(liveData) { results ->
|
return Transformations.map(liveData) { results ->
|
||||||
|
|
|
@ -49,8 +49,11 @@ internal class DefaultFetchEditHistoryTask @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val events = response.chunks.toMutableList()
|
// Filter out edition form other users, and redacted editions
|
||||||
response.originalEvent?.let { events.add(it) }
|
val originalSenderId = response.originalEvent?.senderId
|
||||||
return events
|
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 {
|
override suspend fun execute(params: FindReactionEventForUndoTask.Params): FindReactionEventForUndoTask.Result {
|
||||||
val eventId = Realm.getInstance(monarchy.realmConfiguration).use { realm ->
|
val eventId = Realm.getInstance(monarchy.realmConfiguration).use { realm ->
|
||||||
getReactionToRedact(realm, params.reaction, params.eventId)?.eventId
|
getReactionToRedact(realm, params)?.eventId
|
||||||
}
|
}
|
||||||
return FindReactionEventForUndoTask.Result(eventId)
|
return FindReactionEventForUndoTask.Result(eventId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getReactionToRedact(realm: Realm, reaction: String, eventId: String): EventEntity? {
|
private fun getReactionToRedact(realm: Realm, params: FindReactionEventForUndoTask.Params): EventEntity? {
|
||||||
val summary = EventAnnotationsSummaryEntity.where(realm, eventId).findFirst() ?: return null
|
val summary = EventAnnotationsSummaryEntity.where(realm, params.roomId, params.eventId).findFirst() ?: return null
|
||||||
|
|
||||||
val rase = summary.reactionsSummary.where()
|
val rase = summary.reactionsSummary.where()
|
||||||
.equalTo(ReactionAggregatedSummaryEntityFields.KEY, reaction)
|
.equalTo(ReactionAggregatedSummaryEntityFields.KEY, params.reaction)
|
||||||
.findFirst() ?: return null
|
.findFirst() ?: return null
|
||||||
|
|
||||||
// want to find the event originated by me!
|
// 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 {
|
override suspend fun execute(params: UpdateQuickReactionTask.Params): UpdateQuickReactionTask.Result {
|
||||||
var res: Pair<String?, List<String>?>? = null
|
var res: Pair<String?, List<String>?>? = null
|
||||||
monarchy.doWithRealm { realm ->
|
monarchy.doWithRealm { realm ->
|
||||||
res = updateQuickReaction(realm, params.reaction, params.oppositeReaction, params.eventId)
|
res = updateQuickReaction(realm, params)
|
||||||
}
|
}
|
||||||
return UpdateQuickReactionTask.Result(res?.first, res?.second.orEmpty())
|
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
|
// the emoji reaction has been selected, we need to check if we have reacted it or not
|
||||||
val existingSummary = EventAnnotationsSummaryEntity.where(realm, eventId).findFirst()
|
val existingSummary = EventAnnotationsSummaryEntity.where(realm, params.roomId, params.eventId).findFirst()
|
||||||
?: return Pair(reaction, null)
|
?: return Pair(params.reaction, null)
|
||||||
|
|
||||||
// Ok there is already reactions on this event, have we reacted to it
|
// Ok there is already reactions on this event, have we reacted to it
|
||||||
val aggregationForReaction = existingSummary.reactionsSummary.where()
|
val aggregationForReaction = existingSummary.reactionsSummary.where()
|
||||||
.equalTo(ReactionAggregatedSummaryEntityFields.KEY, reaction)
|
.equalTo(ReactionAggregatedSummaryEntityFields.KEY, params.reaction)
|
||||||
.findFirst()
|
.findFirst()
|
||||||
val aggregationForOppositeReaction = existingSummary.reactionsSummary.where()
|
val aggregationForOppositeReaction = existingSummary.reactionsSummary.where()
|
||||||
.equalTo(ReactionAggregatedSummaryEntityFields.KEY, oppositeReaction)
|
.equalTo(ReactionAggregatedSummaryEntityFields.KEY, params.oppositeReaction)
|
||||||
.findFirst()
|
.findFirst()
|
||||||
|
|
||||||
if (aggregationForReaction == null || !aggregationForReaction.addedByMe) {
|
if (aggregationForReaction == null || !aggregationForReaction.addedByMe) {
|
||||||
|
@ -72,7 +72,7 @@ internal class DefaultUpdateQuickReactionTask @Inject constructor(@SessionDataba
|
||||||
val entity = EventEntity.where(realm, it).findFirst()
|
val entity = EventEntity.where(realm, it).findFirst()
|
||||||
if (entity?.sender == userId) entity.eventId else null
|
if (entity?.sender == userId) entity.eventId else null
|
||||||
}
|
}
|
||||||
return Pair(reaction, toRedact)
|
return Pair(params.reaction, toRedact)
|
||||||
} else {
|
} else {
|
||||||
// I already added it, so i need to undo it (like a toggle)
|
// I already added it, so i need to undo it (like a toggle)
|
||||||
// find all m.redaction coming from me to readact them
|
// 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.PowerLevelsContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
|
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.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.MessageType
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.OptionItem
|
import org.matrix.android.sdk.api.session.room.model.message.OptionItem
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.getFileUrl
|
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.send.UserDraft
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
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.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.getRelationContent
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.getTextEditableContent
|
import org.matrix.android.sdk.api.session.room.timeline.getTextEditableContent
|
||||||
import org.matrix.android.sdk.api.session.widgets.model.Widget
|
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())
|
room.editReply(state.sendMode.timelineEvent, it, action.text.toString())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val messageContent: MessageContent? =
|
val messageContent = state.sendMode.timelineEvent.getLastMessageContent()
|
||||||
state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel()
|
|
||||||
?: state.sendMode.timelineEvent.root.getClearContent().toModel()
|
|
||||||
val existingBody = messageContent?.body ?: ""
|
val existingBody = messageContent?.body ?: ""
|
||||||
if (existingBody != action.text) {
|
if (existingBody != action.text) {
|
||||||
room.editTextMessage(state.sendMode.timelineEvent.root.eventId ?: "",
|
room.editTextMessage(state.sendMode.timelineEvent.root.eventId ?: "",
|
||||||
|
@ -842,9 +840,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
popDraft()
|
popDraft()
|
||||||
}
|
}
|
||||||
is SendMode.QUOTE -> {
|
is SendMode.QUOTE -> {
|
||||||
val messageContent: MessageContent? =
|
val messageContent = state.sendMode.timelineEvent.getLastMessageContent()
|
||||||
state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel()
|
|
||||||
?: state.sendMode.timelineEvent.root.getClearContent().toModel()
|
|
||||||
val textMsg = messageContent?.body
|
val textMsg = messageContent?.body
|
||||||
|
|
||||||
val finalText = legacyRiotQuoteText(textMsg, action.text.toString())
|
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> {
|
private fun actionsForEvent(timelineEvent: TimelineEvent, actionPermissions: ActionPermissions): List<EventSharedAction> {
|
||||||
val messageContent: MessageContent? = timelineEvent.annotations?.editSummary?.aggregatedContent.toModel()
|
val messageContent = timelineEvent.getLastMessageContent()
|
||||||
?: timelineEvent.root.getClearContent().toModel()
|
|
||||||
val msgType = messageContent?.msgType
|
val msgType = messageContent?.msgType
|
||||||
|
|
||||||
return arrayListOf<EventSharedAction>().apply {
|
return arrayListOf<EventSharedAction>().apply {
|
||||||
|
|
|
@ -15,42 +15,28 @@
|
||||||
*/
|
*/
|
||||||
package im.vector.app.features.home.room.detail.timeline.edithistory
|
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.Fail
|
||||||
import com.airbnb.mvrx.FragmentViewModelContext
|
import com.airbnb.mvrx.FragmentViewModelContext
|
||||||
import com.airbnb.mvrx.Loading
|
import com.airbnb.mvrx.Loading
|
||||||
import com.airbnb.mvrx.MvRxState
|
|
||||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||||
import com.airbnb.mvrx.Success
|
import com.airbnb.mvrx.Success
|
||||||
import com.airbnb.mvrx.Uninitialized
|
|
||||||
import com.airbnb.mvrx.ViewModelContext
|
import com.airbnb.mvrx.ViewModelContext
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedInject
|
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
|
import dagger.assisted.AssistedInject
|
||||||
import im.vector.app.core.date.VectorDateFormatter
|
import im.vector.app.core.date.VectorDateFormatter
|
||||||
import im.vector.app.core.platform.EmptyAction
|
import im.vector.app.core.platform.EmptyAction
|
||||||
import im.vector.app.core.platform.EmptyViewEvents
|
import im.vector.app.core.platform.EmptyViewEvents
|
||||||
import im.vector.app.core.platform.VectorViewModel
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
import im.vector.app.features.home.room.detail.timeline.action.TimelineEventFragmentArgs
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
|
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.api.session.events.model.isReply
|
||||||
import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult
|
import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.UUID
|
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
|
class ViewEditHistoryViewModel @AssistedInject constructor(@Assisted
|
||||||
initialState: ViewEditHistoryViewState,
|
initialState: ViewEditHistoryViewState,
|
||||||
val session: Session,
|
val session: Session,
|
||||||
|
@ -82,48 +68,48 @@ class ViewEditHistoryViewModel @AssistedInject constructor(@Assisted
|
||||||
|
|
||||||
private fun loadHistory() {
|
private fun loadHistory() {
|
||||||
setState { copy(editList = Loading()) }
|
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 {
|
setState {
|
||||||
copy(editList = Fail(failure))
|
copy(editList = Fail(failure))
|
||||||
}
|
}
|
||||||
|
return@launch
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSuccess(data: List<Event>) {
|
var originalIsReply = false
|
||||||
var originalIsReply = false
|
|
||||||
|
|
||||||
val events = data.map { event ->
|
data.forEach { event ->
|
||||||
val timelineID = event.roomId + UUID.randomUUID().toString()
|
val timelineID = event.roomId + UUID.randomUUID().toString()
|
||||||
event.also {
|
// We need to check encryption
|
||||||
// We need to check encryption
|
if (event.isEncrypted() && event.mxDecryptionResult == null) {
|
||||||
if (it.isEncrypted() && it.mxDecryptionResult == null) {
|
// for now decrypt sync
|
||||||
// for now decrypt sync
|
try {
|
||||||
try {
|
val result = session.cryptoService().decryptEvent(event, timelineID)
|
||||||
val result = session.cryptoService().decryptEvent(it, timelineID)
|
event.mxDecryptionResult = OlmDecryptionResult(
|
||||||
it.mxDecryptionResult = OlmDecryptionResult(
|
payload = result.clearEvent,
|
||||||
payload = result.clearEvent,
|
senderKey = result.senderCurve25519Key,
|
||||||
senderKey = result.senderCurve25519Key,
|
keysClaimed = result.claimedEd25519Key?.let { k -> mapOf("ed25519" to k) },
|
||||||
keysClaimed = result.claimedEd25519Key?.let { k -> mapOf("ed25519" to k) },
|
forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain
|
||||||
forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain
|
)
|
||||||
)
|
} catch (e: MXCryptoError) {
|
||||||
} catch (e: MXCryptoError) {
|
Timber.w("Failed to decrypt event in history")
|
||||||
Timber.w("Failed to decrypt event in history")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.eventId == it.eventId) {
|
|
||||||
originalIsReply = it.isReply()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setState {
|
|
||||||
copy(
|
if (event.eventId == eventId) {
|
||||||
editList = Success(events),
|
originalIsReply = event.isReply()
|
||||||
isOriginalAReply = originalIsReply
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
setState {
|
||||||
|
copy(
|
||||||
|
editList = Success(data),
|
||||||
|
isOriginalAReply = originalIsReply
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handle(action: EmptyAction) {
|
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.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 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.Session
|
||||||
import org.matrix.android.sdk.api.session.crypto.verification.CancelCode
|
import org.matrix.android.sdk.api.session.crypto.verification.CancelCode
|
||||||
import org.matrix.android.sdk.api.session.crypto.verification.safeValueOf
|
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.MessageRelationContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationCancelContent
|
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.api.session.room.timeline.TimelineEvent
|
||||||
import org.matrix.android.sdk.internal.session.room.VerificationState
|
|
||||||
import javax.inject.Inject
|
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.ReadReceiptData
|
||||||
import im.vector.app.features.home.room.detail.timeline.item.ReferencesInfoData
|
import im.vector.app.features.home.room.detail.timeline.item.ReferencesInfoData
|
||||||
import im.vector.app.features.settings.VectorPreferences
|
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.extensions.orFalse
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
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.getLastMessageContent
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.hasBeenEdited
|
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.crypto.model.event.EncryptedEventContent
|
||||||
import org.matrix.android.sdk.internal.session.room.VerificationState
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -18,9 +18,9 @@ package im.vector.app.features.home.room.detail.timeline.item
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import kotlinx.parcelize.Parcelize
|
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.session.room.send.SendState
|
||||||
import org.matrix.android.sdk.api.util.MatrixItem
|
import org.matrix.android.sdk.api.util.MatrixItem
|
||||||
import org.matrix.android.sdk.internal.session.room.VerificationState
|
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class MessageInformationData(
|
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.RoomDetailAction
|
||||||
import im.vector.app.features.home.room.detail.timeline.MessageColorProvider
|
import im.vector.app.features.home.room.detail.timeline.MessageColorProvider
|
||||||
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
|
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.api.session.crypto.verification.VerificationService
|
||||||
import org.matrix.android.sdk.internal.session.room.VerificationState
|
|
||||||
|
|
||||||
@EpoxyModelClass(layout = R.layout.item_timeline_event_base_state)
|
@EpoxyModelClass(layout = R.layout.item_timeline_event_base_state)
|
||||||
abstract class VerificationRequestItem : AbsBaseMessageItem<VerificationRequestItem.Holder>() {
|
abstract class VerificationRequestItem : AbsBaseMessageItem<VerificationRequestItem.Holder>() {
|
||||||
|
|
|
@ -48,6 +48,7 @@ class HomeserverSettingsController @Inject constructor(
|
||||||
data ?: return
|
data ?: return
|
||||||
|
|
||||||
buildHeader(data)
|
buildHeader(data)
|
||||||
|
buildCapabilities(data)
|
||||||
when (val federationVersion = data.federationVersion) {
|
when (val federationVersion = data.federationVersion) {
|
||||||
is Loading,
|
is Loading,
|
||||||
is Uninitialized ->
|
is Uninitialized ->
|
||||||
|
@ -63,7 +64,6 @@ class HomeserverSettingsController @Inject constructor(
|
||||||
is Success ->
|
is Success ->
|
||||||
buildFederationVersion(federationVersion())
|
buildFederationVersion(federationVersion())
|
||||||
}
|
}
|
||||||
buildCapabilities(data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildHeader(state: HomeServerSettingsViewState) {
|
private fun buildHeader(state: HomeServerSettingsViewState) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue