mirror of
https://github.com/element-hq/element-android
synced 2024-11-24 10:25:35 +03:00
Add is_falling_back support for rich thread replies
Enhance thread awareness handler so normal replies with thread disabled will be visible in te appropriate thread Fix conflicts
This commit is contained in:
parent
21111922e6
commit
a758ad71e6
13 changed files with 91 additions and 59 deletions
|
@ -389,7 +389,7 @@ fun Event.isReply(): Boolean {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Event.isReplyRenderedInThread(): Boolean {
|
fun Event.isReplyRenderedInThread(): Boolean {
|
||||||
return isReply() && getRelationContent()?.inReplyTo?.shouldRenderInThread() == true
|
return isReply() && getRelationContent()?.shouldRenderInThread() == true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Event.isThread(): Boolean = getRelationContentForType(RelationType.THREAD)?.eventId != null
|
fun Event.isThread(): Boolean = getRelationContentForType(RelationType.THREAD)?.eventId != null
|
||||||
|
|
|
@ -26,5 +26,6 @@ data class ReactionInfo(
|
||||||
@Json(name = "key") val key: String,
|
@Json(name = "key") val key: String,
|
||||||
// always null for reaction
|
// always null for reaction
|
||||||
@Json(name = "m.in_reply_to") override val inReplyTo: ReplyToContent? = null,
|
@Json(name = "m.in_reply_to") override val inReplyTo: ReplyToContent? = null,
|
||||||
@Json(name = "option") override val option: Int? = null
|
@Json(name = "option") override val option: Int? = null,
|
||||||
|
@Json(name = "is_falling_back") override val isFallingBack: Boolean? = null
|
||||||
) : RelationContent
|
) : RelationContent
|
||||||
|
|
|
@ -24,4 +24,5 @@ interface RelationContent {
|
||||||
val eventId: String?
|
val eventId: String?
|
||||||
val inReplyTo: ReplyToContent?
|
val inReplyTo: ReplyToContent?
|
||||||
val option: Int?
|
val option: Int?
|
||||||
|
val isFallingBack: Boolean? // Thread fallback to differentiate replies within threads
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,5 +23,8 @@ data class RelationDefaultContent(
|
||||||
@Json(name = "rel_type") override val type: String?,
|
@Json(name = "rel_type") override val type: String?,
|
||||||
@Json(name = "event_id") override val eventId: String?,
|
@Json(name = "event_id") override val eventId: String?,
|
||||||
@Json(name = "m.in_reply_to") override val inReplyTo: ReplyToContent? = null,
|
@Json(name = "m.in_reply_to") override val inReplyTo: ReplyToContent? = null,
|
||||||
@Json(name = "option") override val option: Int? = null
|
@Json(name = "option") override val option: Int? = null,
|
||||||
|
@Json(name = "is_falling_back") override val isFallingBack: Boolean? = null
|
||||||
) : RelationContent
|
) : RelationContent
|
||||||
|
|
||||||
|
fun RelationDefaultContent.shouldRenderInThread(): Boolean = isFallingBack == false
|
||||||
|
|
|
@ -21,8 +21,5 @@ import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class ReplyToContent(
|
data class ReplyToContent(
|
||||||
@Json(name = "event_id") val eventId: String? = null,
|
@Json(name = "event_id") val eventId: String? = null
|
||||||
@Json(name = "render_in") val renderIn: List<String>? = null
|
|
||||||
)
|
)
|
||||||
|
|
||||||
fun ReplyToContent.shouldRenderInThread(): Boolean = renderIn?.contains("m.thread") == true
|
|
||||||
|
|
|
@ -127,7 +127,7 @@ private fun EventEntity.toTimelineEventEntity(roomMemberContentsByUser: HashMap<
|
||||||
return timelineEventEntity
|
return timelineEventEntity
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun ThreadSummaryEntity.Companion.createOrUpdate(
|
internal suspend fun ThreadSummaryEntity.Companion.createOrUpdate(
|
||||||
threadSummaryType: ThreadSummaryUpdateType,
|
threadSummaryType: ThreadSummaryUpdateType,
|
||||||
realm: Realm,
|
realm: Realm,
|
||||||
roomId: String,
|
roomId: String,
|
||||||
|
@ -204,7 +204,7 @@ internal fun ThreadSummaryEntity.Companion.createOrUpdate(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun decryptIfNeeded(cryptoService: CryptoService?, eventEntity: EventEntity, roomId: String) {
|
private suspend fun decryptIfNeeded(cryptoService: CryptoService?, eventEntity: EventEntity, roomId: String) {
|
||||||
cryptoService ?: return
|
cryptoService ?: return
|
||||||
val event = eventEntity.asDomain()
|
val event = eventEntity.asDomain()
|
||||||
if (event.isEncrypted() && event.mxDecryptionResult == null && event.eventId != null) {
|
if (event.isEncrypted() && event.mxDecryptionResult == null && event.eventId != null) {
|
||||||
|
|
|
@ -172,7 +172,7 @@ internal class DefaultRelationService @AssistedInject constructor(
|
||||||
replyText = replyInThreadText,
|
replyText = replyInThreadText,
|
||||||
autoMarkdown = autoMarkdown,
|
autoMarkdown = autoMarkdown,
|
||||||
rootThreadEventId = rootThreadEventId,
|
rootThreadEventId = rootThreadEventId,
|
||||||
showInThread = true
|
showInThread = false
|
||||||
)
|
)
|
||||||
?.also {
|
?.also {
|
||||||
saveLocalEcho(it)
|
saveLocalEcho(it)
|
||||||
|
|
|
@ -100,7 +100,7 @@ internal class EventEditor @Inject constructor(private val eventSenderProcessor:
|
||||||
eventReplied = originalTimelineEvent,
|
eventReplied = originalTimelineEvent,
|
||||||
replyText = newBodyText,
|
replyText = newBodyText,
|
||||||
autoMarkdown = false,
|
autoMarkdown = false,
|
||||||
showInThread = false
|
showInThread = false // Test that value
|
||||||
)?.copy(
|
)?.copy(
|
||||||
eventId = replyToEdit.eventId
|
eventId = replyToEdit.eventId
|
||||||
) ?: return NoOpCancellable
|
) ?: return NoOpCancellable
|
||||||
|
|
|
@ -560,7 +560,7 @@ internal class LocalEchoEventFactory @Inject constructor(
|
||||||
relatesTo = generateReplyRelationContent(
|
relatesTo = generateReplyRelationContent(
|
||||||
eventId = eventId,
|
eventId = eventId,
|
||||||
rootThreadEventId = rootThreadEventId,
|
rootThreadEventId = rootThreadEventId,
|
||||||
showAsReply = showInThread))
|
showInThread = showInThread))
|
||||||
return createMessageEvent(roomId, content)
|
return createMessageEvent(roomId, content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -570,18 +570,20 @@ internal class LocalEchoEventFactory @Inject constructor(
|
||||||
* "m.relates_to": {
|
* "m.relates_to": {
|
||||||
* "rel_type": "m.thread",
|
* "rel_type": "m.thread",
|
||||||
* "event_id": "$thread_root",
|
* "event_id": "$thread_root",
|
||||||
|
* "is_falling_back": false,
|
||||||
* "m.in_reply_to": {
|
* "m.in_reply_to": {
|
||||||
* "event_id": "$event_target",
|
* "event_id": "$event_target"
|
||||||
* "render_in": ["m.thread"]
|
* }
|
||||||
* }
|
* }
|
||||||
* }
|
|
||||||
*/
|
*/
|
||||||
private fun generateReplyRelationContent(eventId: String, rootThreadEventId: String? = null, showAsReply: Boolean): RelationDefaultContent =
|
private fun generateReplyRelationContent(eventId: String, rootThreadEventId: String? = null, showInThread: Boolean): RelationDefaultContent =
|
||||||
rootThreadEventId?.let {
|
rootThreadEventId?.let {
|
||||||
RelationDefaultContent(
|
RelationDefaultContent(
|
||||||
type = RelationType.THREAD,
|
type = RelationType.THREAD,
|
||||||
eventId = it,
|
eventId = it,
|
||||||
inReplyTo = ReplyToContent(eventId = eventId, renderIn = if (showAsReply) arrayListOf("m.thread") else null))
|
isFallingBack = showInThread,
|
||||||
|
// False when is a rich reply from within a thread, and true when is a reply that should be visible from threads
|
||||||
|
inReplyTo = ReplyToContent(eventId = eventId))
|
||||||
} ?: RelationDefaultContent(null, null, ReplyToContent(eventId = eventId))
|
} ?: RelationDefaultContent(null, null, ReplyToContent(eventId = eventId))
|
||||||
|
|
||||||
private fun buildFormattedReply(permalink: String, userLink: String, userId: String, bodyFormatted: String, newBodyFormatted: String): String {
|
private fun buildFormattedReply(permalink: String, userLink: String, userId: String, bodyFormatted: String, newBodyFormatted: String): String {
|
||||||
|
|
|
@ -60,6 +60,7 @@ fun TextContent.toThreadTextContent(
|
||||||
relatesTo = RelationDefaultContent(
|
relatesTo = RelationDefaultContent(
|
||||||
type = RelationType.THREAD,
|
type = RelationType.THREAD,
|
||||||
eventId = rootThreadEventId,
|
eventId = rootThreadEventId,
|
||||||
|
isFallingBack = true,
|
||||||
inReplyTo = ReplyToContent(
|
inReplyTo = ReplyToContent(
|
||||||
eventId = latestThreadEventId
|
eventId = latestThreadEventId
|
||||||
)),
|
)),
|
||||||
|
|
|
@ -102,11 +102,11 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
||||||
data class LEFT(val data: Map<String, RoomSync>) : HandlingStrategy()
|
data class LEFT(val data: Map<String, RoomSync>) : HandlingStrategy()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handle(realm: Realm,
|
suspend fun handle(realm: Realm,
|
||||||
roomsSyncResponse: RoomsSyncResponse,
|
roomsSyncResponse: RoomsSyncResponse,
|
||||||
isInitialSync: Boolean,
|
isInitialSync: Boolean,
|
||||||
aggregator: SyncResponsePostTreatmentAggregator,
|
aggregator: SyncResponsePostTreatmentAggregator,
|
||||||
reporter: ProgressReporter? = null) {
|
reporter: ProgressReporter? = null) {
|
||||||
Timber.v("Execute transaction from $this")
|
Timber.v("Execute transaction from $this")
|
||||||
handleRoomSync(realm, HandlingStrategy.JOINED(roomsSyncResponse.join), isInitialSync, aggregator, reporter)
|
handleRoomSync(realm, HandlingStrategy.JOINED(roomsSyncResponse.join), isInitialSync, aggregator, reporter)
|
||||||
handleRoomSync(realm, HandlingStrategy.INVITED(roomsSyncResponse.invite), isInitialSync, aggregator, reporter)
|
handleRoomSync(realm, HandlingStrategy.INVITED(roomsSyncResponse.invite), isInitialSync, aggregator, reporter)
|
||||||
|
@ -121,11 +121,11 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
||||||
}
|
}
|
||||||
// PRIVATE METHODS *****************************************************************************
|
// PRIVATE METHODS *****************************************************************************
|
||||||
|
|
||||||
private fun handleRoomSync(realm: Realm,
|
private suspend fun handleRoomSync(realm: Realm,
|
||||||
handlingStrategy: HandlingStrategy,
|
handlingStrategy: HandlingStrategy,
|
||||||
isInitialSync: Boolean,
|
isInitialSync: Boolean,
|
||||||
aggregator: SyncResponsePostTreatmentAggregator,
|
aggregator: SyncResponsePostTreatmentAggregator,
|
||||||
reporter: ProgressReporter?) {
|
reporter: ProgressReporter?) {
|
||||||
val insertType = if (isInitialSync) {
|
val insertType = if (isInitialSync) {
|
||||||
EventInsertType.INITIAL_SYNC
|
EventInsertType.INITIAL_SYNC
|
||||||
} else {
|
} else {
|
||||||
|
@ -158,11 +158,11 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
||||||
realm.insertOrUpdate(rooms)
|
realm.insertOrUpdate(rooms)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun insertJoinRoomsFromInitSync(realm: Realm,
|
private suspend fun insertJoinRoomsFromInitSync(realm: Realm,
|
||||||
handlingStrategy: HandlingStrategy.JOINED,
|
handlingStrategy: HandlingStrategy.JOINED,
|
||||||
syncLocalTimeStampMillis: Long,
|
syncLocalTimeStampMillis: Long,
|
||||||
aggregator: SyncResponsePostTreatmentAggregator,
|
aggregator: SyncResponsePostTreatmentAggregator,
|
||||||
reporter: ProgressReporter?) {
|
reporter: ProgressReporter?) {
|
||||||
val bestChunkSize = computeBestChunkSize(
|
val bestChunkSize = computeBestChunkSize(
|
||||||
listSize = handlingStrategy.data.keys.size,
|
listSize = handlingStrategy.data.keys.size,
|
||||||
limit = (initialSyncStrategy as? InitialSyncStrategy.Optimized)?.maxRoomsToInsert ?: Int.MAX_VALUE
|
limit = (initialSyncStrategy as? InitialSyncStrategy.Optimized)?.maxRoomsToInsert ?: Int.MAX_VALUE
|
||||||
|
@ -200,12 +200,12 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleJoinedRoom(realm: Realm,
|
private suspend fun handleJoinedRoom(realm: Realm,
|
||||||
roomId: String,
|
roomId: String,
|
||||||
roomSync: RoomSync,
|
roomSync: RoomSync,
|
||||||
insertType: EventInsertType,
|
insertType: EventInsertType,
|
||||||
syncLocalTimestampMillis: Long,
|
syncLocalTimestampMillis: Long,
|
||||||
aggregator: SyncResponsePostTreatmentAggregator): RoomEntity {
|
aggregator: SyncResponsePostTreatmentAggregator): RoomEntity {
|
||||||
Timber.v("Handle join sync for room $roomId")
|
Timber.v("Handle join sync for room $roomId")
|
||||||
|
|
||||||
val ephemeralResult = (roomSync.ephemeral as? LazyRoomSyncEphemeral.Parsed)
|
val ephemeralResult = (roomSync.ephemeral as? LazyRoomSyncEphemeral.Parsed)
|
||||||
|
@ -351,15 +351,15 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
||||||
return roomEntity
|
return roomEntity
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleTimelineEvents(realm: Realm,
|
private suspend fun handleTimelineEvents(realm: Realm,
|
||||||
roomId: String,
|
roomId: String,
|
||||||
roomEntity: RoomEntity,
|
roomEntity: RoomEntity,
|
||||||
eventList: List<Event>,
|
eventList: List<Event>,
|
||||||
prevToken: String? = null,
|
prevToken: String? = null,
|
||||||
isLimited: Boolean = true,
|
isLimited: Boolean = true,
|
||||||
insertType: EventInsertType,
|
insertType: EventInsertType,
|
||||||
syncLocalTimestampMillis: Long,
|
syncLocalTimestampMillis: Long,
|
||||||
aggregator: SyncResponsePostTreatmentAggregator): ChunkEntity {
|
aggregator: SyncResponsePostTreatmentAggregator): ChunkEntity {
|
||||||
val lastChunk = ChunkEntity.findLastForwardChunkOfRoom(realm, roomEntity.roomId)
|
val lastChunk = ChunkEntity.findLastForwardChunkOfRoom(realm, roomEntity.roomId)
|
||||||
if (isLimited && lastChunk != null) {
|
if (isLimited && lastChunk != null) {
|
||||||
lastChunk.deleteOnCascade(deleteStateEvents = false, canDeleteRoot = true)
|
lastChunk.deleteOnCascade(deleteStateEvents = false, canDeleteRoot = true)
|
||||||
|
|
|
@ -32,6 +32,7 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageFormat
|
||||||
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.MessageTextContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent
|
||||||
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.relation.RelationDefaultContent
|
||||||
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.session.sync.model.SyncResponse
|
import org.matrix.android.sdk.api.session.sync.model.SyncResponse
|
||||||
import org.matrix.android.sdk.api.util.JsonDict
|
import org.matrix.android.sdk.api.util.JsonDict
|
||||||
|
@ -170,8 +171,9 @@ internal class ThreadsAwarenessHandler @Inject constructor(
|
||||||
event.mxDecryptionResult?.payload?.toMutableMap() ?: return null
|
event.mxDecryptionResult?.payload?.toMutableMap() ?: return null
|
||||||
}
|
}
|
||||||
val eventBody = event.getDecryptedTextSummary() ?: return null
|
val eventBody = event.getDecryptedTextSummary() ?: return null
|
||||||
|
val threadRelation = getRootThreadRelationContent(event)
|
||||||
val eventIdToInject = getPreviousEventOrRoot(event) ?: run {
|
val eventIdToInject = getPreviousEventOrRoot(event) ?: run {
|
||||||
return@makeEventThreadAware injectFallbackIndicator(event, eventBody, eventEntity, eventPayload)
|
return@makeEventThreadAware injectFallbackIndicator(event, eventBody, eventEntity, eventPayload, threadRelation)
|
||||||
}
|
}
|
||||||
val eventToInject = getEventFromDB(realm, eventIdToInject)
|
val eventToInject = getEventFromDB(realm, eventIdToInject)
|
||||||
val eventToInjectBody = eventToInject?.getDecryptedTextSummary()
|
val eventToInjectBody = eventToInject?.getDecryptedTextSummary()
|
||||||
|
@ -183,17 +185,19 @@ internal class ThreadsAwarenessHandler @Inject constructor(
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
eventBody = eventBody,
|
eventBody = eventBody,
|
||||||
eventToInject = eventToInject,
|
eventToInject = eventToInject,
|
||||||
eventToInjectBody = eventToInjectBody) ?: return null
|
eventToInjectBody = eventToInjectBody,
|
||||||
|
threadRelation = threadRelation) ?: return null
|
||||||
|
|
||||||
// update the event
|
// update the event
|
||||||
contentForNonEncrypted = updateEventEntity(event, eventEntity, eventPayload, messageTextContent)
|
contentForNonEncrypted = updateEventEntity(event, eventEntity, eventPayload, messageTextContent)
|
||||||
} else {
|
} else {
|
||||||
contentForNonEncrypted = injectFallbackIndicator(event, eventBody, eventEntity, eventPayload)
|
contentForNonEncrypted = injectFallbackIndicator(event, eventBody, eventEntity, eventPayload, threadRelation)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now lets try to find relations for improved results, while some events may come with reverse order
|
// Now lets try to find relations for improved results, while some events may come with reverse order
|
||||||
eventEntity?.let {
|
eventEntity?.let {
|
||||||
// When eventEntity is not null means that we are not from within roomSyncHandler
|
// When eventEntity is not null means that we are not from within roomSyncHandler
|
||||||
handleEventsThatRelatesTo(realm, roomId, event, eventBody, false)
|
handleEventsThatRelatesTo(realm, roomId, event, eventBody, false, threadRelation)
|
||||||
}
|
}
|
||||||
return contentForNonEncrypted
|
return contentForNonEncrypted
|
||||||
}
|
}
|
||||||
|
@ -205,11 +209,16 @@ internal class ThreadsAwarenessHandler @Inject constructor(
|
||||||
* @param event the current event received
|
* @param event the current event received
|
||||||
* @return The content to inject in the roomSyncHandler live events
|
* @return The content to inject in the roomSyncHandler live events
|
||||||
*/
|
*/
|
||||||
private fun handleRootThreadEventsIfNeeded(realm: Realm, roomId: String, eventEntity: EventEntity?, event: Event): String? {
|
private fun handleRootThreadEventsIfNeeded(
|
||||||
|
realm: Realm,
|
||||||
|
roomId: String,
|
||||||
|
eventEntity: EventEntity?,
|
||||||
|
event: Event
|
||||||
|
): String? {
|
||||||
if (!isThreadEvent(event) && cacheEventRootId.contains(eventEntity?.eventId)) {
|
if (!isThreadEvent(event) && cacheEventRootId.contains(eventEntity?.eventId)) {
|
||||||
eventEntity?.let {
|
eventEntity?.let {
|
||||||
val eventBody = event.getDecryptedTextSummary() ?: return null
|
val eventBody = event.getDecryptedTextSummary() ?: return null
|
||||||
return handleEventsThatRelatesTo(realm, roomId, event, eventBody, true)
|
return handleEventsThatRelatesTo(realm, roomId, event, eventBody, true, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
|
@ -224,7 +233,14 @@ internal class ThreadsAwarenessHandler @Inject constructor(
|
||||||
* @param isFromCache determines whether or not we already know this is root thread event
|
* @param isFromCache determines whether or not we already know this is root thread event
|
||||||
* @return The content to inject in the roomSyncHandler live events
|
* @return The content to inject in the roomSyncHandler live events
|
||||||
*/
|
*/
|
||||||
private fun handleEventsThatRelatesTo(realm: Realm, roomId: String, event: Event, eventBody: String, isFromCache: Boolean): String? {
|
private fun handleEventsThatRelatesTo(
|
||||||
|
realm: Realm,
|
||||||
|
roomId: String,
|
||||||
|
event: Event,
|
||||||
|
eventBody: String,
|
||||||
|
isFromCache: Boolean,
|
||||||
|
threadRelation: RelationDefaultContent?
|
||||||
|
): String? {
|
||||||
event.eventId ?: return null
|
event.eventId ?: return null
|
||||||
val rootThreadEventId = if (isFromCache) event.eventId else event.getRootThreadEventId() ?: return null
|
val rootThreadEventId = if (isFromCache) event.eventId else event.getRootThreadEventId() ?: return null
|
||||||
eventThatRelatesTo(realm, event.eventId, rootThreadEventId)?.forEach { eventEntityFound ->
|
eventThatRelatesTo(realm, event.eventId, rootThreadEventId)?.forEach { eventEntityFound ->
|
||||||
|
@ -236,7 +252,8 @@ internal class ThreadsAwarenessHandler @Inject constructor(
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
eventBody = newEventBody,
|
eventBody = newEventBody,
|
||||||
eventToInject = event,
|
eventToInject = event,
|
||||||
eventToInjectBody = eventBody) ?: return null
|
eventToInjectBody = eventBody,
|
||||||
|
threadRelation = threadRelation) ?: return null
|
||||||
|
|
||||||
return updateEventEntity(newEventFound, eventEntityFound, newEventPayload, messageTextContent)
|
return updateEventEntity(newEventFound, eventEntityFound, newEventPayload, messageTextContent)
|
||||||
}
|
}
|
||||||
|
@ -280,7 +297,9 @@ internal class ThreadsAwarenessHandler @Inject constructor(
|
||||||
private fun injectEvent(roomId: String,
|
private fun injectEvent(roomId: String,
|
||||||
eventBody: String,
|
eventBody: String,
|
||||||
eventToInject: Event,
|
eventToInject: Event,
|
||||||
eventToInjectBody: String): Content? {
|
eventToInjectBody: String,
|
||||||
|
threadRelation: RelationDefaultContent?
|
||||||
|
): Content? {
|
||||||
val eventToInjectId = eventToInject.eventId ?: return null
|
val eventToInjectId = eventToInject.eventId ?: return null
|
||||||
val eventIdToInjectSenderId = eventToInject.senderId.orEmpty()
|
val eventIdToInjectSenderId = eventToInject.senderId.orEmpty()
|
||||||
val permalink = permalinkFactory.createPermalink(roomId, eventToInjectId, false)
|
val permalink = permalinkFactory.createPermalink(roomId, eventToInjectId, false)
|
||||||
|
@ -293,6 +312,7 @@ internal class ThreadsAwarenessHandler @Inject constructor(
|
||||||
eventBody)
|
eventBody)
|
||||||
|
|
||||||
return MessageTextContent(
|
return MessageTextContent(
|
||||||
|
relatesTo = threadRelation,
|
||||||
msgType = MessageType.MSGTYPE_TEXT,
|
msgType = MessageType.MSGTYPE_TEXT,
|
||||||
format = MessageFormat.FORMAT_MATRIX_HTML,
|
format = MessageFormat.FORMAT_MATRIX_HTML,
|
||||||
body = eventBody,
|
body = eventBody,
|
||||||
|
@ -306,12 +326,14 @@ internal class ThreadsAwarenessHandler @Inject constructor(
|
||||||
private fun injectFallbackIndicator(event: Event,
|
private fun injectFallbackIndicator(event: Event,
|
||||||
eventBody: String,
|
eventBody: String,
|
||||||
eventEntity: EventEntity?,
|
eventEntity: EventEntity?,
|
||||||
eventPayload: MutableMap<String, Any>): String? {
|
eventPayload: MutableMap<String, Any>,
|
||||||
|
threadRelation: RelationDefaultContent?): String? {
|
||||||
val replyFormatted = LocalEchoEventFactory.QUOTE_PATTERN.format(
|
val replyFormatted = LocalEchoEventFactory.QUOTE_PATTERN.format(
|
||||||
"In reply to a thread",
|
"In reply to a thread",
|
||||||
eventBody)
|
eventBody)
|
||||||
|
|
||||||
val messageTextContent = MessageTextContent(
|
val messageTextContent = MessageTextContent(
|
||||||
|
relatesTo = threadRelation,
|
||||||
msgType = MessageType.MSGTYPE_TEXT,
|
msgType = MessageType.MSGTYPE_TEXT,
|
||||||
format = MessageFormat.FORMAT_MATRIX_HTML,
|
format = MessageFormat.FORMAT_MATRIX_HTML,
|
||||||
body = eventBody,
|
body = eventBody,
|
||||||
|
@ -359,6 +381,9 @@ internal class ThreadsAwarenessHandler @Inject constructor(
|
||||||
private fun getRootThreadEventId(event: Event): String? =
|
private fun getRootThreadEventId(event: Event): String? =
|
||||||
event.content.toModel<MessageRelationContent>()?.relatesTo?.eventId
|
event.content.toModel<MessageRelationContent>()?.relatesTo?.eventId
|
||||||
|
|
||||||
|
private fun getRootThreadRelationContent(event: Event): RelationDefaultContent? =
|
||||||
|
event.content.toModel<MessageRelationContent>()?.relatesTo
|
||||||
|
|
||||||
private fun getPreviousEventOrRoot(event: Event): String? =
|
private fun getPreviousEventOrRoot(event: Event): String? =
|
||||||
event.content.toModel<MessageRelationContent>()?.relatesTo?.inReplyTo?.eventId
|
event.content.toModel<MessageRelationContent>()?.relatesTo?.inReplyTo?.eventId
|
||||||
|
|
||||||
|
|
|
@ -465,7 +465,8 @@ class MessageComposerViewModel @AssistedInject constructor(
|
||||||
// is original event a reply?
|
// is original event a reply?
|
||||||
val relationContent = state.sendMode.timelineEvent.getRelationContent()
|
val relationContent = state.sendMode.timelineEvent.getRelationContent()
|
||||||
val inReplyTo = if (state.rootThreadEventId != null) {
|
val inReplyTo = if (state.rootThreadEventId != null) {
|
||||||
if (relationContent?.inReplyTo?.shouldRenderInThread() == true) {
|
// Thread event
|
||||||
|
if (relationContent?.shouldRenderInThread() == true) {
|
||||||
// Reply within a thread event
|
// Reply within a thread event
|
||||||
relationContent.inReplyTo?.eventId
|
relationContent.inReplyTo?.eventId
|
||||||
} else {
|
} else {
|
||||||
|
@ -509,6 +510,7 @@ class MessageComposerViewModel @AssistedInject constructor(
|
||||||
is SendMode.Reply -> {
|
is SendMode.Reply -> {
|
||||||
val timelineEvent = state.sendMode.timelineEvent
|
val timelineEvent = state.sendMode.timelineEvent
|
||||||
val showInThread = state.sendMode.timelineEvent.root.isThread() && state.rootThreadEventId == null
|
val showInThread = state.sendMode.timelineEvent.root.isThread() && state.rootThreadEventId == null
|
||||||
|
// If threads are disabled this will make the fallback replies visible to clients with threads enabled
|
||||||
val rootThreadEventId = if (showInThread) timelineEvent.root.getRootThreadEventId() else null
|
val rootThreadEventId = if (showInThread) timelineEvent.root.getRootThreadEventId() else null
|
||||||
state.rootThreadEventId?.let {
|
state.rootThreadEventId?.let {
|
||||||
room.replyInThread(
|
room.replyInThread(
|
||||||
|
|
Loading…
Reference in a new issue