mirror of
https://github.com/element-hq/element-android
synced 2024-11-27 20:06:51 +03:00
Merge pull request #243 from vector-im/feature/reply_e2e
Reply in e2e room
This commit is contained in:
commit
419ef7b46f
9 changed files with 138 additions and 104 deletions
|
@ -25,6 +25,7 @@ import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
|
||||||
import im.vector.matrix.android.internal.di.MoshiProvider
|
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.collections.HashMap
|
||||||
|
|
||||||
typealias Content = JsonDict
|
typealias Content = JsonDict
|
||||||
|
|
||||||
|
@ -146,21 +147,27 @@ data class Event(
|
||||||
val adapter = MoshiProvider.providesMoshi().adapter(Event::class.java)
|
val adapter = MoshiProvider.providesMoshi().adapter(Event::class.java)
|
||||||
mClearEvent = adapter.fromJsonValue(decryptionResult.clearEvent)
|
mClearEvent = adapter.fromJsonValue(decryptionResult.clearEvent)
|
||||||
|
|
||||||
}
|
if (mClearEvent != null) {
|
||||||
mClearEvent?.apply {
|
mSenderCurve25519Key = decryptionResult.senderCurve25519Key
|
||||||
mSenderCurve25519Key = decryptionResult.senderCurve25519Key
|
mClaimedEd25519Key = decryptionResult.claimedEd25519Key
|
||||||
mClaimedEd25519Key = decryptionResult.claimedEd25519Key
|
mForwardingCurve25519KeyChain = decryptionResult.forwardingCurve25519KeyChain
|
||||||
mForwardingCurve25519KeyChain = decryptionResult.forwardingCurve25519KeyChain
|
|
||||||
try {
|
// For encrypted events with relation, the m.relates_to is kept in clear, so we need to put it back
|
||||||
// Add "m.relates_to" data from e2e event to the unencrypted event
|
// in the clear event
|
||||||
// TODO
|
try {
|
||||||
//if (getWireContent().getAsJsonObject().has("m.relates_to")) {
|
content?.get("m.relates_to")?.let { clearRelates ->
|
||||||
// clearEvent!!.getContentAsJsonObject()
|
mClearEvent = mClearEvent?.copy(
|
||||||
// .add("m.relates_to", getWireContent().getAsJsonObject().get("m.relates_to"))
|
content = HashMap(mClearEvent!!.content).apply {
|
||||||
//}
|
this["m.relates_to"] = clearRelates
|
||||||
} catch (e: Exception) {
|
}
|
||||||
Timber.e(e, "Unable to restore 'm.relates_to' the clear event")
|
)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e, "Unable to restore 'm.relates_to' the clear event")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mCryptoError = null
|
mCryptoError = null
|
||||||
|
|
|
@ -69,7 +69,7 @@ internal class RoomFactory @Inject constructor(private val context: Context,
|
||||||
val stateService = DefaultStateService(roomId, taskExecutor, sendStateTask)
|
val stateService = DefaultStateService(roomId, taskExecutor, sendStateTask)
|
||||||
val roomMembersService = DefaultMembershipService(roomId, monarchy, taskExecutor, loadRoomMembersTask, inviteTask, joinRoomTask, leaveRoomTask)
|
val roomMembersService = DefaultMembershipService(roomId, monarchy, taskExecutor, loadRoomMembersTask, inviteTask, joinRoomTask, leaveRoomTask)
|
||||||
val readService = DefaultReadService(roomId, monarchy, taskExecutor, setReadMarkersTask, credentials)
|
val readService = DefaultReadService(roomId, monarchy, taskExecutor, setReadMarkersTask, credentials)
|
||||||
val relationService = DefaultRelationService(context, credentials, roomId, eventFactory, findReactionEventForUndoTask, monarchy, taskExecutor)
|
val relationService = DefaultRelationService(context, credentials, roomId, eventFactory, cryptoService, findReactionEventForUndoTask, monarchy, taskExecutor)
|
||||||
|
|
||||||
return DefaultRoom(
|
return DefaultRoom(
|
||||||
roomId,
|
roomId,
|
||||||
|
|
|
@ -21,6 +21,7 @@ import androidx.work.OneTimeWorkRequest
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.auth.data.Credentials
|
import im.vector.matrix.android.api.auth.data.Credentials
|
||||||
|
import im.vector.matrix.android.api.session.crypto.CryptoService
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
|
import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
|
||||||
import im.vector.matrix.android.api.session.room.model.message.MessageType
|
import im.vector.matrix.android.api.session.room.model.message.MessageType
|
||||||
|
@ -33,6 +34,7 @@ import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryE
|
||||||
import im.vector.matrix.android.internal.database.model.RoomEntity
|
import im.vector.matrix.android.internal.database.model.RoomEntity
|
||||||
import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom
|
import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom
|
||||||
import im.vector.matrix.android.internal.database.query.where
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
|
import im.vector.matrix.android.internal.session.room.send.EncryptEventWorker
|
||||||
import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory
|
import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory
|
||||||
import im.vector.matrix.android.internal.session.room.send.RedactEventWorker
|
import im.vector.matrix.android.internal.session.room.send.RedactEventWorker
|
||||||
import im.vector.matrix.android.internal.session.room.send.SendEventWorker
|
import im.vector.matrix.android.internal.session.room.send.SendEventWorker
|
||||||
|
@ -49,12 +51,12 @@ internal class DefaultRelationService @Inject constructor(private val context: C
|
||||||
private val credentials: Credentials,
|
private val credentials: Credentials,
|
||||||
private val roomId: String,
|
private val roomId: String,
|
||||||
private val eventFactory: LocalEchoEventFactory,
|
private val eventFactory: LocalEchoEventFactory,
|
||||||
|
private val cryptoService: CryptoService,
|
||||||
private val findReactionEventForUndoTask: FindReactionEventForUndoTask,
|
private val findReactionEventForUndoTask: FindReactionEventForUndoTask,
|
||||||
private val monarchy: Monarchy,
|
private val monarchy: Monarchy,
|
||||||
private val taskExecutor: TaskExecutor)
|
private val taskExecutor: TaskExecutor)
|
||||||
: RelationService {
|
: RelationService {
|
||||||
|
|
||||||
|
|
||||||
override fun sendReaction(reaction: String, targetEventId: String): Cancelable {
|
override fun sendReaction(reaction: String, targetEventId: String): Cancelable {
|
||||||
val event = eventFactory.createReactionEvent(roomId, targetEventId, reaction)
|
val event = eventFactory.createReactionEvent(roomId, targetEventId, reaction)
|
||||||
.also {
|
.also {
|
||||||
|
@ -65,13 +67,8 @@ internal class DefaultRelationService @Inject constructor(private val context: C
|
||||||
return CancelableWork(context, sendRelationWork.id)
|
return CancelableWork(context, sendRelationWork.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun createSendRelationWork(event: Event): OneTimeWorkRequest {
|
private fun createSendRelationWork(event: Event): OneTimeWorkRequest {
|
||||||
val sendContentWorkerParams = SendEventWorker.Params(credentials.userId, roomId, event)
|
return createSendEventWork(event)
|
||||||
val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
|
|
||||||
|
|
||||||
return TimelineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun undoReaction(reaction: String, targetEventId: String, myUserId: String)/*: Cancelable*/ {
|
override fun undoReaction(reaction: String, targetEventId: String, myUserId: String)/*: Cancelable*/ {
|
||||||
|
@ -119,31 +116,44 @@ internal class DefaultRelationService @Inject constructor(private val context: C
|
||||||
val event = eventFactory.createReplaceTextEvent(roomId, targetEventId, newBodyText, newBodyAutoMarkdown, MessageType.MSGTYPE_TEXT, compatibilityBodyText).also {
|
val event = eventFactory.createReplaceTextEvent(roomId, targetEventId, newBodyText, newBodyAutoMarkdown, MessageType.MSGTYPE_TEXT, compatibilityBodyText).also {
|
||||||
saveLocalEcho(it)
|
saveLocalEcho(it)
|
||||||
}
|
}
|
||||||
val sendContentWorkerParams = SendEventWorker.Params(credentials.userId, roomId, event)
|
val workRequest = createSendEventWork(event)
|
||||||
val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
|
|
||||||
|
|
||||||
//TODO use relation API?
|
|
||||||
|
|
||||||
val workRequest = TimelineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData)
|
|
||||||
TimelineSendEventWorkCommon.postWork(context, roomId, workRequest)
|
TimelineSendEventWorkCommon.postWork(context, roomId, workRequest)
|
||||||
return CancelableWork(context, workRequest.id)
|
return CancelableWork(context, workRequest.id)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun replyToMessage(eventReplied: Event, replyText: String): Cancelable? {
|
override fun replyToMessage(eventReplied: Event, replyText: String): Cancelable? {
|
||||||
val event = eventFactory.createReplyTextEvent(roomId, eventReplied, replyText)?.also {
|
val event = eventFactory.createReplyTextEvent(roomId, eventReplied, replyText)?.also {
|
||||||
saveLocalEcho(it)
|
saveLocalEcho(it)
|
||||||
} ?: return null
|
} ?: return null
|
||||||
val sendContentWorkerParams = SendEventWorker.Params(credentials.userId, roomId, event)
|
|
||||||
val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
|
|
||||||
|
|
||||||
|
if (cryptoService.isRoomEncrypted(roomId)) {
|
||||||
|
val encryptWork = createEncryptEventWork(event, listOf("m.relates_to"))
|
||||||
|
val workRequest = createSendEventWork(event)
|
||||||
|
TimelineSendEventWorkCommon.postSequentialWorks(context, roomId, encryptWork, workRequest)
|
||||||
|
return CancelableWork(context, encryptWork.id)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
val workRequest = createSendEventWork(event)
|
||||||
|
TimelineSendEventWorkCommon.postWork(context, roomId, workRequest)
|
||||||
|
return CancelableWork(context, workRequest.id)
|
||||||
|
}
|
||||||
|
|
||||||
val workRequest = TimelineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData)
|
|
||||||
TimelineSendEventWorkCommon.postWork(context, roomId, workRequest)
|
|
||||||
return CancelableWork(context, workRequest.id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun createEncryptEventWork(event: Event, keepKeys: List<String>?): OneTimeWorkRequest {
|
||||||
|
// Same parameter
|
||||||
|
val params = EncryptEventWorker.Params(credentials.userId, roomId, event, keepKeys)
|
||||||
|
val sendWorkData = WorkerParamsFactory.toData(params)
|
||||||
|
return TimelineSendEventWorkCommon.createWork<EncryptEventWorker>(sendWorkData)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createSendEventWork(event: Event): OneTimeWorkRequest {
|
||||||
|
val sendContentWorkerParams = SendEventWorker.Params(credentials.userId, roomId, event)
|
||||||
|
val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
|
||||||
|
val workRequest = TimelineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData)
|
||||||
|
return workRequest
|
||||||
|
}
|
||||||
|
|
||||||
override fun getEventSummaryLive(eventId: String): LiveData<List<EventAnnotationsSummary>> {
|
override fun getEventSummaryLive(eventId: String): LiveData<List<EventAnnotationsSummary>> {
|
||||||
return monarchy.findAllMappedWithChanges(
|
return monarchy.findAllMappedWithChanges(
|
||||||
|
|
|
@ -39,7 +39,9 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
|
||||||
internal data class Params(
|
internal data class Params(
|
||||||
override val userId: String,
|
override val userId: String,
|
||||||
val roomId: String,
|
val roomId: String,
|
||||||
val event: Event
|
val event: Event,
|
||||||
|
/**Do not encrypt these keys, keep them as is in encrypted content (e.g. m.relates_to)*/
|
||||||
|
val keepKeys: List<String>? = null
|
||||||
) : SessionWorkerParams
|
) : SessionWorkerParams
|
||||||
|
|
||||||
@Inject lateinit var crypto: CryptoService
|
@Inject lateinit var crypto: CryptoService
|
||||||
|
@ -65,8 +67,13 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
|
||||||
var result: MXEncryptEventContentResult? = null
|
var result: MXEncryptEventContentResult? = null
|
||||||
var error: Throwable? = null
|
var error: Throwable? = null
|
||||||
|
|
||||||
|
val localMutableContent = HashMap(localEvent.content)
|
||||||
|
params.keepKeys?.forEach {
|
||||||
|
localMutableContent.remove(it)
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
crypto.encryptEventContent(localEvent.content!!, localEvent.type, params.roomId, object : MatrixCallback<MXEncryptEventContentResult> {
|
crypto.encryptEventContent(localMutableContent, localEvent.type, params.roomId, object : MatrixCallback<MXEncryptEventContentResult> {
|
||||||
override fun onSuccess(data: MXEncryptEventContentResult) {
|
override fun onSuccess(data: MXEncryptEventContentResult) {
|
||||||
result = data
|
result = data
|
||||||
latch.countDown()
|
latch.countDown()
|
||||||
|
@ -83,15 +90,24 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
|
||||||
}
|
}
|
||||||
latch.await()
|
latch.await()
|
||||||
|
|
||||||
val safeResult = result
|
if (result != null) {
|
||||||
if (safeResult != null) {
|
var modifiedContent = HashMap(result?.eventContent)
|
||||||
|
params.keepKeys?.forEach { toKeep ->
|
||||||
|
localEvent.content?.get(toKeep)?.let {
|
||||||
|
//put it back in the encrypted thing
|
||||||
|
modifiedContent[toKeep] = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val safeResult = result!!.copy(eventContent = modifiedContent)
|
||||||
val encryptedEvent = localEvent.copy(
|
val encryptedEvent = localEvent.copy(
|
||||||
type = safeResult.eventType,
|
type = safeResult.eventType,
|
||||||
content = safeResult.eventContent
|
content = safeResult.eventContent
|
||||||
)
|
)
|
||||||
val nextWorkerParams = SendEventWorker.Params(params.userId, params.roomId, encryptedEvent)
|
val nextWorkerParams = SendEventWorker.Params(params.userId, params.roomId, encryptedEvent)
|
||||||
return Result.success(WorkerParamsFactory.toData(nextWorkerParams))
|
return Result.success(WorkerParamsFactory.toData(nextWorkerParams))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val safeError = error
|
val safeError = error
|
||||||
val sendState = when (safeError) {
|
val sendState = when (safeError) {
|
||||||
is Failure.CryptoError -> SendState.FAILED_UNKNOWN_DEVICES
|
is Failure.CryptoError -> SendState.FAILED_UNKNOWN_DEVICES
|
||||||
|
|
|
@ -100,7 +100,7 @@ object JsonCanonicalizer {
|
||||||
|
|
||||||
return result.toString()
|
return result.toString()
|
||||||
}
|
}
|
||||||
is String -> return "\"" + src.toString() + "\""
|
is String -> return JSONObject.quote(src)
|
||||||
else -> return src.toString()
|
else -> return src.toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -247,7 +247,7 @@ class RoomDetailFragment :
|
||||||
//TODO this is used at several places, find way to refactor?
|
//TODO this is used at several places, find way to refactor?
|
||||||
val messageContent: MessageContent? =
|
val messageContent: MessageContent? =
|
||||||
event.annotations?.editSummary?.aggregatedContent?.toModel()
|
event.annotations?.editSummary?.aggregatedContent?.toModel()
|
||||||
?: event.root.content.toModel()
|
?: event.root.getClearContent().toModel()
|
||||||
val nonFormattedBody = messageContent?.body ?: ""
|
val nonFormattedBody = messageContent?.body ?: ""
|
||||||
var formattedBody: CharSequence? = null
|
var formattedBody: CharSequence? = null
|
||||||
if (messageContent is MessageTextContent && messageContent.format == MessageType.FORMAT_MATRIX_HTML) {
|
if (messageContent is MessageTextContent && messageContent.format == MessageType.FORMAT_MATRIX_HTML) {
|
||||||
|
|
|
@ -45,8 +45,10 @@ import javax.inject.Inject
|
||||||
*/
|
*/
|
||||||
class MessageActionsBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
class MessageActionsBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
||||||
|
|
||||||
@Inject lateinit var messageActionViewModelFactory: MessageActionsViewModel.Factory
|
@Inject
|
||||||
@Inject lateinit var avatarRenderer: AvatarRenderer
|
lateinit var messageActionViewModelFactory: MessageActionsViewModel.Factory
|
||||||
|
@Inject
|
||||||
|
lateinit var avatarRenderer: AvatarRenderer
|
||||||
private val viewModel: MessageActionsViewModel by fragmentViewModel(MessageActionsViewModel::class)
|
private val viewModel: MessageActionsViewModel by fragmentViewModel(MessageActionsViewModel::class)
|
||||||
|
|
||||||
private lateinit var actionHandlerModel: ActionsHandler
|
private lateinit var actionHandlerModel: ActionsHandler
|
||||||
|
@ -124,17 +126,18 @@ class MessageActionsBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun invalidate() = withState(viewModel) {
|
override fun invalidate() = withState(viewModel) {
|
||||||
if (it.showPreview) {
|
val body = viewModel.resolveBody(it)
|
||||||
|
if (body != null) {
|
||||||
bottom_sheet_message_preview.isVisible = true
|
bottom_sheet_message_preview.isVisible = true
|
||||||
senderNameTextView.text = it.senderName
|
senderNameTextView.text = it.senderName()
|
||||||
messageBodyTextView.text = it.messageBody
|
messageBodyTextView.text = body
|
||||||
messageTimestampText.text = it.ts
|
messageTimestampText.text = it.time()
|
||||||
avatarRenderer.render(it.senderAvatarPath, it.userId, it.senderName, senderAvatarImageView)
|
avatarRenderer.render(it.informationData.avatarUrl, it.informationData.senderId, it.senderName(), senderAvatarImageView)
|
||||||
} else {
|
} else {
|
||||||
bottom_sheet_message_preview.isVisible = false
|
bottom_sheet_message_preview.isVisible = false
|
||||||
}
|
}
|
||||||
quickReactBottomDivider.isVisible = it.canReact
|
quickReactBottomDivider.isVisible = it.canReact()
|
||||||
bottom_sheet_quick_reaction_container.isVisible = it.canReact
|
bottom_sheet_quick_reaction_container.isVisible = it.canReact()
|
||||||
return@withState
|
return@withState
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,8 @@ import im.vector.matrix.android.api.session.events.model.toModel
|
||||||
import im.vector.matrix.android.api.session.room.model.message.MessageContent
|
import im.vector.matrix.android.api.session.room.model.message.MessageContent
|
||||||
import im.vector.matrix.android.api.session.room.model.message.MessageTextContent
|
import im.vector.matrix.android.api.session.room.model.message.MessageTextContent
|
||||||
import im.vector.matrix.android.api.session.room.model.message.MessageType
|
import im.vector.matrix.android.api.session.room.model.message.MessageType
|
||||||
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
|
import im.vector.riotredesign.core.di.HasScreenInjector
|
||||||
import im.vector.riotredesign.core.platform.VectorViewModel
|
import im.vector.riotredesign.core.platform.VectorViewModel
|
||||||
import im.vector.riotredesign.features.home.room.detail.timeline.format.NoticeEventFormatter
|
import im.vector.riotredesign.features.home.room.detail.timeline.format.NoticeEventFormatter
|
||||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
|
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
|
||||||
|
@ -35,21 +37,46 @@ import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
|
val dateFormat = SimpleDateFormat("EEE, d MMM yyyy HH:mm", Locale.getDefault())
|
||||||
|
|
||||||
data class MessageActionState(
|
data class MessageActionState(
|
||||||
val roomId: String,
|
val roomId: String,
|
||||||
val eventId: String,
|
val eventId: String,
|
||||||
val informationData: MessageInformationData,
|
val informationData: MessageInformationData,
|
||||||
val userId: String = "",
|
val timelineEvent: TimelineEvent?
|
||||||
val senderName: String = "",
|
) : MvRxState {
|
||||||
val messageBody: CharSequence? = null,
|
|
||||||
val ts: String? = null,
|
|
||||||
val showPreview: Boolean = false,
|
|
||||||
val canReact: Boolean = false,
|
|
||||||
val senderAvatarPath: String? = null)
|
|
||||||
: MvRxState {
|
|
||||||
|
|
||||||
constructor(args: TimelineEventFragmentArgs) : this(roomId = args.roomId, eventId = args.eventId, informationData = args.informationData)
|
fun senderName(): String = informationData.memberName?.toString() ?: ""
|
||||||
|
|
||||||
|
fun time(): String? = timelineEvent?.root?.originServerTs?.let { dateFormat.format(Date(it)) }
|
||||||
|
?: ""
|
||||||
|
|
||||||
|
fun canReact(): Boolean = timelineEvent?.root?.type == EventType.MESSAGE && timelineEvent.sendState.isSent()
|
||||||
|
|
||||||
|
fun messageBody(eventHtmlRenderer: EventHtmlRenderer?, noticeEventFormatter: NoticeEventFormatter?): CharSequence? {
|
||||||
|
return when (timelineEvent?.root?.getClearType()) {
|
||||||
|
EventType.MESSAGE -> {
|
||||||
|
val messageContent: MessageContent? = timelineEvent.annotations?.editSummary?.aggregatedContent?.toModel()
|
||||||
|
?: timelineEvent.root.getClearContent().toModel()
|
||||||
|
if (messageContent is MessageTextContent && messageContent.format == MessageType.FORMAT_MATRIX_HTML) {
|
||||||
|
eventHtmlRenderer?.render(messageContent.formattedBody
|
||||||
|
?: messageContent.body)
|
||||||
|
} else {
|
||||||
|
messageContent?.body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EventType.STATE_ROOM_NAME,
|
||||||
|
EventType.STATE_ROOM_TOPIC,
|
||||||
|
EventType.STATE_ROOM_MEMBER,
|
||||||
|
EventType.STATE_HISTORY_VISIBILITY,
|
||||||
|
EventType.CALL_INVITE,
|
||||||
|
EventType.CALL_HANGUP,
|
||||||
|
EventType.CALL_ANSWER -> {
|
||||||
|
noticeEventFormatter?.format(timelineEvent)
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -62,10 +89,6 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
||||||
private val noticeEventFormatter: NoticeEventFormatter
|
private val noticeEventFormatter: NoticeEventFormatter
|
||||||
) : VectorViewModel<MessageActionState>(initialState) {
|
) : VectorViewModel<MessageActionState>(initialState) {
|
||||||
|
|
||||||
private val roomId = initialState.roomId
|
|
||||||
private val eventId = initialState.eventId
|
|
||||||
private val informationData = initialState.informationData
|
|
||||||
|
|
||||||
@AssistedInject.Factory
|
@AssistedInject.Factory
|
||||||
interface Factory {
|
interface Factory {
|
||||||
fun create(initialState: MessageActionState): MessageActionsViewModel
|
fun create(initialState: MessageActionState): MessageActionsViewModel
|
||||||
|
@ -77,47 +100,23 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
||||||
val fragment: MessageActionsBottomSheet = (viewModelContext as FragmentViewModelContext).fragment()
|
val fragment: MessageActionsBottomSheet = (viewModelContext as FragmentViewModelContext).fragment()
|
||||||
return fragment.messageActionViewModelFactory.create(state)
|
return fragment.messageActionViewModelFactory.create(state)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
override fun initialState(viewModelContext: ViewModelContext): MessageActionState? {
|
||||||
init {
|
val session = (viewModelContext.activity as HasScreenInjector).injector().session()
|
||||||
setState { reduceState(this) }
|
val args: TimelineEventFragmentArgs = viewModelContext.args()
|
||||||
}
|
val event = session.getRoom(args.roomId)?.getTimeLineEvent(args.eventId)
|
||||||
|
return MessageActionState(
|
||||||
private fun reduceState(state: MessageActionState): MessageActionState {
|
args.roomId,
|
||||||
val dateFormat = SimpleDateFormat("EEE, d MMM yyyy HH:mm", Locale.getDefault())
|
args.eventId,
|
||||||
val event = session.getRoom(roomId)?.getTimeLineEvent(eventId) ?: return state
|
args.informationData,
|
||||||
var body: CharSequence? = null
|
event
|
||||||
val originTs = event.root.originServerTs
|
)
|
||||||
when (event.root.getClearType()) {
|
|
||||||
EventType.MESSAGE -> {
|
|
||||||
val messageContent: MessageContent? = event.annotations?.editSummary?.aggregatedContent?.toModel()
|
|
||||||
?: event.root.getClearContent().toModel()
|
|
||||||
body = messageContent?.body
|
|
||||||
if (messageContent is MessageTextContent && messageContent.format == MessageType.FORMAT_MATRIX_HTML) {
|
|
||||||
body = eventHtmlRenderer.render(messageContent.formattedBody
|
|
||||||
?: messageContent.body)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EventType.STATE_ROOM_NAME,
|
|
||||||
EventType.STATE_ROOM_TOPIC,
|
|
||||||
EventType.STATE_ROOM_MEMBER,
|
|
||||||
EventType.STATE_HISTORY_VISIBILITY,
|
|
||||||
EventType.CALL_INVITE,
|
|
||||||
EventType.CALL_HANGUP,
|
|
||||||
EventType.CALL_ANSWER -> {
|
|
||||||
body = noticeEventFormatter.format(event)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return state.copy(
|
|
||||||
userId = event.root.senderId ?: "",
|
}
|
||||||
senderName = informationData.memberName?.toString() ?: "",
|
|
||||||
messageBody = body,
|
fun resolveBody(state: MessageActionState): CharSequence? {
|
||||||
ts = dateFormat.format(Date(originTs ?: 0)),
|
return state.messageBody(eventHtmlRenderer, noticeEventFormatter)
|
||||||
showPreview = body != null,
|
|
||||||
canReact = event.root.type == EventType.MESSAGE && event.sendState.isSent(),
|
|
||||||
senderAvatarPath = informationData.avatarUrl
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -33,11 +33,10 @@ import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
||||||
import im.vector.riotredesign.core.platform.VectorViewModel
|
import im.vector.riotredesign.core.platform.VectorViewModel
|
||||||
import im.vector.riotredesign.core.resources.StringProvider
|
import im.vector.riotredesign.core.resources.StringProvider
|
||||||
|
import im.vector.riotredesign.core.utils.isSingleEmoji
|
||||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
|
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
|
||||||
import im.vector.riotredesign.core.utils.isSingleEmoji
|
|
||||||
|
|
||||||
|
|
||||||
data class SimpleAction(val uid: String, val titleRes: Int, val iconResId: Int?, val data: Any? = null)
|
data class SimpleAction(val uid: String, val titleRes: Int, val iconResId: Int?, val data: Any? = null)
|
||||||
|
|
||||||
|
@ -95,7 +94,7 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M
|
||||||
val event = session.getRoom(state.roomId)?.getTimeLineEvent(state.eventId) ?: return state
|
val event = session.getRoom(state.roomId)?.getTimeLineEvent(state.eventId) ?: return state
|
||||||
|
|
||||||
val messageContent: MessageContent? = event.annotations?.editSummary?.aggregatedContent?.toModel()
|
val messageContent: MessageContent? = event.annotations?.editSummary?.aggregatedContent?.toModel()
|
||||||
?: event.root.content.toModel()
|
?: event.root.getClearContent().toModel()
|
||||||
val type = messageContent?.type
|
val type = messageContent?.type
|
||||||
|
|
||||||
val actions = if (!event.sendState.isSent()) {
|
val actions = if (!event.sendState.isSent()) {
|
||||||
|
|
Loading…
Reference in a new issue