BayernMessenger/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt

678 lines
29 KiB
Kotlin
Raw Normal View History

2019-01-18 10:12:08 +00:00
/*
* Copyright 2019 New Vector Ltd
2019-01-18 10:12:08 +00:00
*
* 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
2019-01-18 10:12:08 +00:00
*
* 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.
2019-01-18 10:12:08 +00:00
*/
package im.vector.riotx.features.home.room.detail
2018-12-29 16:54:03 +00:00
2019-07-02 12:59:44 +00:00
import android.net.Uri
2019-05-23 14:44:51 +00:00
import android.text.TextUtils
import androidx.annotation.IdRes
2019-04-09 11:36:33 +00:00
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.airbnb.mvrx.FragmentViewModelContext
2018-12-29 16:54:03 +00:00
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.ViewModelContext
import com.jakewharton.rxrelay2.BehaviorRelay
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
2018-12-29 16:54:03 +00:00
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.MatrixPatterns
2018-12-29 16:54:03 +00:00
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.content.ContentAttachmentData
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.isImageMessage
import im.vector.matrix.android.api.session.events.model.isTextMessage
2019-05-23 14:44:51 +00:00
import im.vector.matrix.android.api.session.events.model.toModel
2019-07-08 17:06:17 +00:00
import im.vector.matrix.android.api.session.file.FileService
import im.vector.matrix.android.api.session.room.model.Membership
2019-05-23 14:44:51 +00:00
import im.vector.matrix.android.api.session.room.model.message.MessageContent
2019-04-09 15:53:23 +00:00
import im.vector.matrix.android.api.session.room.model.message.MessageType
2019-07-09 12:48:57 +00:00
import im.vector.matrix.android.api.session.room.model.message.getFileUrl
import im.vector.matrix.android.api.session.room.model.tombstone.RoomTombstoneContent
2019-05-23 14:44:51 +00:00
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.api.session.room.timeline.TimelineSettings
2019-07-08 17:06:17 +00:00
import im.vector.matrix.android.internal.crypto.attachments.toElementToDecrypt
import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent
2018-12-29 16:54:03 +00:00
import im.vector.matrix.rx.rx
import im.vector.riotx.R
import im.vector.riotx.core.extensions.postLiveEvent
import im.vector.riotx.core.intent.getFilenameFromUri
import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.core.resources.UserPreferencesProvider
import im.vector.riotx.core.utils.LiveEvent
import im.vector.riotx.core.utils.subscribeLogError
import im.vector.riotx.features.command.CommandParser
import im.vector.riotx.features.command.ParsedCommand
import im.vector.riotx.features.home.room.detail.timeline.helper.TimelineDisplayableEvents
2019-08-08 10:09:05 +00:00
import im.vector.riotx.features.settings.VectorPreferences
import io.reactivex.rxkotlin.subscribeBy
2019-05-23 14:44:51 +00:00
import org.commonmark.parser.Parser
import org.commonmark.renderer.html.HtmlRenderer
2019-06-20 14:27:43 +00:00
import timber.log.Timber
2019-07-08 17:06:17 +00:00
import java.io.File
import java.util.concurrent.TimeUnit
2018-12-29 16:54:03 +00:00
2019-05-23 14:44:51 +00:00
class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: RoomDetailViewState,
private val userPreferencesProvider: UserPreferencesProvider,
2019-08-08 10:09:05 +00:00
private val vectorPreferences: VectorPreferences,
private val session: Session
2019-04-05 08:40:59 +00:00
) : VectorViewModel<RoomDetailViewState>(initialState) {
2018-12-29 16:54:03 +00:00
private val room = session.getRoom(initialState.roomId)!!
private val roomId = initialState.roomId
private val eventId = initialState.eventId
private val displayedEventsObservable = BehaviorRelay.create<RoomDetailActions.EventDisplayed>()
private val timelineSettings = if (userPreferencesProvider.shouldShowHiddenEvents()) {
TimelineSettings(30, false, true, TimelineDisplayableEvents.DEBUG_DISPLAYABLE_TYPES, userPreferencesProvider.shouldShowReadReceipts())
2019-06-07 11:55:32 +00:00
} else {
TimelineSettings(30, true, true, TimelineDisplayableEvents.DISPLAYABLE_TYPES, userPreferencesProvider.shouldShowReadReceipts())
2019-06-07 11:55:32 +00:00
}
private var timeline = room.createTimeline(eventId, timelineSettings)
2019-07-09 12:52:48 +00:00
// Slot to keep a pending action during permission request
var pendingAction: RoomDetailActions? = null
@AssistedInject.Factory
interface Factory {
fun create(initialState: RoomDetailViewState): RoomDetailViewModel
}
companion object : MvRxViewModelFactory<RoomDetailViewModel, RoomDetailViewState> {
2018-12-29 16:54:03 +00:00
const val PAGINATION_COUNT = 50
2018-12-29 16:54:03 +00:00
@JvmStatic
override fun create(viewModelContext: ViewModelContext, state: RoomDetailViewState): RoomDetailViewModel? {
val fragment: RoomDetailFragment = (viewModelContext as FragmentViewModelContext).fragment()
return fragment.roomDetailViewModelFactory.create(state)
}
2018-12-29 16:54:03 +00:00
}
init {
observeSyncState()
2018-12-29 16:54:03 +00:00
observeRoomSummary()
observeEventDisplayedActions()
observeSummaryState()
room.rx().loadRoomMembersIfNeeded().subscribeLogError().disposeOnClear()
timeline.start()
setState { copy(timeline = this@RoomDetailViewModel.timeline) }
2018-12-29 16:54:03 +00:00
}
fun process(action: RoomDetailActions) {
2018-12-29 16:54:03 +00:00
when (action) {
is RoomDetailActions.SendMessage -> handleSendMessage(action)
is RoomDetailActions.SendMedia -> handleSendMedia(action)
is RoomDetailActions.EventDisplayed -> handleEventDisplayed(action)
is RoomDetailActions.LoadMoreTimelineEvents -> handleLoadMore(action)
is RoomDetailActions.SendReaction -> handleSendReaction(action)
is RoomDetailActions.AcceptInvite -> handleAcceptInvite()
is RoomDetailActions.RejectInvite -> handleRejectInvite()
is RoomDetailActions.RedactAction -> handleRedactEvent(action)
is RoomDetailActions.UndoReaction -> handleUndoReact(action)
is RoomDetailActions.UpdateQuickReactAction -> handleUpdateQuickReaction(action)
is RoomDetailActions.EnterEditMode -> handleEditAction(action)
is RoomDetailActions.EnterQuoteMode -> handleQuoteAction(action)
is RoomDetailActions.EnterReplyMode -> handleReplyAction(action)
2019-07-08 17:06:17 +00:00
is RoomDetailActions.DownloadFile -> handleDownloadFile(action)
2019-06-20 14:27:43 +00:00
is RoomDetailActions.NavigateToEvent -> handleNavigateToEvent(action)
is RoomDetailActions.HandleTombstoneEvent -> handleTombstoneEvent(action)
is RoomDetailActions.ResendMessage -> handleResendEvent(action)
is RoomDetailActions.RemoveFailedEcho -> handleRemove(action)
is RoomDetailActions.ClearSendQueue -> handleClearSendQueue()
is RoomDetailActions.ResendAll -> handleResendAll()
2019-06-20 14:27:43 +00:00
else -> Timber.e("Unhandled Action: $action")
2018-12-29 16:54:03 +00:00
}
}
private fun handleTombstoneEvent(action: RoomDetailActions.HandleTombstoneEvent) {
val tombstoneContent = action.event.getClearContent().toModel<RoomTombstoneContent>()
?: return
2019-07-31 12:06:10 +00:00
val roomId = tombstoneContent.replacementRoom ?: ""
val isRoomJoined = session.getRoom(roomId)?.roomSummary()?.membership == Membership.JOIN
if (isRoomJoined) {
setState { copy(tombstoneEventHandling = Success(roomId)) }
} else {
val viaServer = MatrixPatterns.extractServerNameFromId(action.event.senderId).let {
if (it.isNullOrBlank()) {
emptyList()
} else {
listOf(it)
}
}
session.rx()
.joinRoom(roomId, viaServer)
.map { roomId }
.execute {
copy(tombstoneEventHandling = it)
}
}
}
private fun enterEditMode(event: TimelineEvent) {
2019-05-23 14:44:51 +00:00
setState {
copy(
sendMode = SendMode.EDIT(event)
2019-05-23 14:44:51 +00:00
)
2018-12-29 16:54:03 +00:00
}
}
2019-05-23 14:44:51 +00:00
fun resetSendMode() {
setState {
copy(
sendMode = SendMode.REGULAR
2019-05-23 14:44:51 +00:00
)
}
}
private val _nonBlockingPopAlert = MutableLiveData<LiveEvent<Pair<Int, List<Any>>>>()
val nonBlockingPopAlert: LiveData<LiveEvent<Pair<Int, List<Any>>>>
get() = _nonBlockingPopAlert
2019-04-09 11:36:33 +00:00
private val _sendMessageResultLiveData = MutableLiveData<LiveEvent<SendMessageResult>>()
val sendMessageResultLiveData: LiveData<LiveEvent<SendMessageResult>>
get() = _sendMessageResultLiveData
2019-06-20 14:27:43 +00:00
private val _navigateToEvent = MutableLiveData<LiveEvent<String>>()
val navigateToEvent: LiveData<LiveEvent<String>>
get() = _navigateToEvent
2019-07-08 17:06:17 +00:00
private val _downloadedFileEvent = MutableLiveData<LiveEvent<DownloadFileState>>()
val downloadedFileEvent: LiveData<LiveEvent<DownloadFileState>>
get() = _downloadedFileEvent
2019-06-20 14:27:43 +00:00
fun isMenuItemVisible(@IdRes itemId: Int): Boolean {
if (itemId == R.id.clear_message_queue) {
//For now always disable, woker cancellation is not working properly
return false//timeline.pendingEventCount() > 0
}
if (itemId == R.id.resend_all) {
return timeline.failedToDeliverEventCount() > 0
}
if (itemId == R.id.clear_all) {
return timeline.failedToDeliverEventCount() > 0
}
return false
}
2018-12-29 16:54:03 +00:00
// PRIVATE METHODS *****************************************************************************
private fun handleSendMessage(action: RoomDetailActions.SendMessage) {
2019-05-23 14:44:51 +00:00
withState { state ->
when (state.sendMode) {
SendMode.REGULAR -> {
2019-05-23 14:44:51 +00:00
val slashCommandResult = CommandParser.parseSplashCommand(action.text)
when (slashCommandResult) {
is ParsedCommand.ErrorNotACommand -> {
2019-05-23 14:44:51 +00:00
// Send the text message to the room
room.sendTextMessage(action.text, autoMarkdown = action.autoMarkdown)
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.MessageSent)
2019-05-23 14:44:51 +00:00
}
is ParsedCommand.ErrorSyntax -> {
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandError(slashCommandResult.command))
2019-05-23 14:44:51 +00:00
}
is ParsedCommand.ErrorEmptySlashCommand -> {
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandUnknown("/"))
2019-05-23 14:44:51 +00:00
}
is ParsedCommand.ErrorUnknownSlashCommand -> {
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandUnknown(slashCommandResult.slashCommand))
2019-05-23 14:44:51 +00:00
}
is ParsedCommand.Invite -> {
2019-05-23 14:44:51 +00:00
handleInviteSlashCommand(slashCommandResult)
}
is ParsedCommand.SetUserPowerLevel -> {
2019-05-23 14:44:51 +00:00
// TODO
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandNotImplemented)
2019-05-23 14:44:51 +00:00
}
is ParsedCommand.ClearScalarToken -> {
2019-05-23 14:44:51 +00:00
// TODO
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandNotImplemented)
2019-05-23 14:44:51 +00:00
}
is ParsedCommand.SetMarkdown -> {
2019-08-08 10:09:05 +00:00
vectorPreferences.setMarkdownEnabled(slashCommandResult.enable)
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandHandled(
if (slashCommandResult.enable) R.string.markdown_has_been_enabled else R.string.markdown_has_been_disabled))
2019-05-23 14:44:51 +00:00
}
is ParsedCommand.UnbanUser -> {
2019-05-23 14:44:51 +00:00
// TODO
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandNotImplemented)
2019-05-23 14:44:51 +00:00
}
is ParsedCommand.BanUser -> {
2019-05-23 14:44:51 +00:00
// TODO
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandNotImplemented)
2019-05-23 14:44:51 +00:00
}
is ParsedCommand.KickUser -> {
2019-05-23 14:44:51 +00:00
// TODO
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandNotImplemented)
2019-05-23 14:44:51 +00:00
}
is ParsedCommand.JoinRoom -> {
2019-05-23 14:44:51 +00:00
// TODO
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandNotImplemented)
2019-05-23 14:44:51 +00:00
}
is ParsedCommand.PartRoom -> {
2019-05-23 14:44:51 +00:00
// TODO
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandNotImplemented)
2019-05-23 14:44:51 +00:00
}
is ParsedCommand.SendEmote -> {
2019-05-23 14:44:51 +00:00
room.sendTextMessage(slashCommandResult.message, msgType = MessageType.MSGTYPE_EMOTE)
2019-08-08 10:09:05 +00:00
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandHandled())
2019-05-23 14:44:51 +00:00
}
is ParsedCommand.ChangeTopic -> {
2019-05-23 14:44:51 +00:00
handleChangeTopicSlashCommand(slashCommandResult)
}
is ParsedCommand.ChangeDisplayName -> {
2019-05-23 14:44:51 +00:00
// TODO
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandNotImplemented)
2019-05-23 14:44:51 +00:00
}
}
}
is SendMode.EDIT -> {
//is original event a reply?
val inReplyTo = state.sendMode.timelineEvent.root.getClearContent().toModel<MessageContent>()?.relatesTo?.inReplyTo?.eventId
?: state.sendMode.timelineEvent.root.content.toModel<EncryptedEventContent>()?.relatesTo?.inReplyTo?.eventId
if (inReplyTo != null) {
//TODO check if same content?
room.getTimeLineEvent(inReplyTo)?.let {
room.editReply(state.sendMode.timelineEvent, it, action.text)
}
} else {
val messageContent: MessageContent? =
state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel()
?: state.sendMode.timelineEvent.root.getClearContent().toModel()
val existingBody = messageContent?.body ?: ""
if (existingBody != action.text) {
room.editTextMessage(state.sendMode.timelineEvent.root.eventId
?: "", messageContent?.type
?: MessageType.MSGTYPE_TEXT, action.text, action.autoMarkdown)
} else {
Timber.w("Same message content, do not send edition")
}
}
2019-05-23 14:44:51 +00:00
setState {
copy(
sendMode = SendMode.REGULAR
2019-05-23 14:44:51 +00:00
)
}
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.MessageSent)
2019-05-23 14:44:51 +00:00
}
is SendMode.QUOTE -> {
val messageContent: MessageContent? =
state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel()
?: state.sendMode.timelineEvent.root.getClearContent().toModel()
val textMsg = messageContent?.body
val finalText = legacyRiotQuoteText(textMsg, action.text)
//TODO Refactor this, just temporary for quotes
val parser = Parser.builder().build()
val document = parser.parse(finalText)
val renderer = HtmlRenderer.builder().build()
val htmlText = renderer.render(document)
if (TextUtils.equals(finalText, htmlText)) {
room.sendTextMessage(finalText)
} else {
room.sendFormattedTextMessage(finalText, htmlText)
}
setState {
copy(
sendMode = SendMode.REGULAR
)
}
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.MessageSent)
}
is SendMode.REPLY -> {
state.sendMode.timelineEvent.let {
room.replyToMessage(it, action.text, action.autoMarkdown)
2019-05-23 14:44:51 +00:00
setState {
copy(
sendMode = SendMode.REGULAR
2019-05-23 14:44:51 +00:00
)
}
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.MessageSent)
2019-05-23 14:44:51 +00:00
}
}
}
}
}
private fun legacyRiotQuoteText(quotedText: String?, myText: String): String {
val messageParagraphs = quotedText?.split("\n\n".toRegex())?.dropLastWhile { it.isEmpty() }?.toTypedArray()
var quotedTextMsg = StringBuilder()
if (messageParagraphs != null) {
for (i in messageParagraphs.indices) {
if (messageParagraphs[i].trim({ it <= ' ' }) != "") {
quotedTextMsg.append("> ").append(messageParagraphs[i])
}
if (i + 1 != messageParagraphs.size) {
quotedTextMsg.append("\n\n")
}
2019-04-09 11:36:33 +00:00
}
}
2019-05-23 14:44:51 +00:00
val finalText = "$quotedTextMsg\n\n$myText"
return finalText
2019-04-09 11:36:33 +00:00
}
2019-04-09 16:33:28 +00:00
private fun handleChangeTopicSlashCommand(changeTopic: ParsedCommand.ChangeTopic) {
2019-08-08 10:09:05 +00:00
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandHandled())
2019-04-09 16:33:28 +00:00
room.updateTopic(changeTopic.topic, object : MatrixCallback<Unit> {
override fun onSuccess(data: Unit) {
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandResultOk)
2019-04-09 16:33:28 +00:00
}
override fun onFailure(failure: Throwable) {
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandResultError(failure))
2019-04-09 16:33:28 +00:00
}
})
}
2019-04-09 12:35:18 +00:00
private fun handleInviteSlashCommand(invite: ParsedCommand.Invite) {
2019-08-08 10:09:05 +00:00
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandHandled())
2019-04-09 12:35:18 +00:00
room.invite(invite.userId, object : MatrixCallback<Unit> {
override fun onSuccess(data: Unit) {
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandResultOk)
2019-04-09 11:36:33 +00:00
}
2019-04-09 12:35:18 +00:00
override fun onFailure(failure: Throwable) {
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandResultError(failure))
2019-04-09 11:36:33 +00:00
}
2019-04-09 12:35:18 +00:00
})
2018-12-29 16:54:03 +00:00
}
private fun handleSendReaction(action: RoomDetailActions.SendReaction) {
room.sendReaction(action.reaction, action.targetEventId)
}
2019-05-17 15:15:44 +00:00
private fun handleRedactEvent(action: RoomDetailActions.RedactAction) {
val event = room.getTimeLineEvent(action.targetEventId) ?: return
room.redactEvent(event.root, action.reason)
}
private fun handleUndoReact(action: RoomDetailActions.UndoReaction) {
room.undoReaction(action.key, action.targetEventId, session.myUserId)
2019-05-17 15:15:44 +00:00
}
private fun handleUpdateQuickReaction(action: RoomDetailActions.UpdateQuickReactAction) {
2019-06-24 14:13:58 +00:00
if (action.add) {
room.sendReaction(action.selectedReaction, action.targetEventId)
} else {
room.undoReaction(action.selectedReaction, action.targetEventId, session.myUserId)
2019-06-24 14:13:58 +00:00
}
}
private fun handleSendMedia(action: RoomDetailActions.SendMedia) {
val attachments = action.mediaFiles.map {
2019-07-02 12:59:44 +00:00
val nameWithExtension = getFilenameFromUri(null, Uri.parse(it.path))
ContentAttachmentData(
size = it.size,
duration = it.duration,
date = it.date,
height = it.height,
width = it.width,
2019-07-02 12:59:44 +00:00
name = nameWithExtension ?: it.name,
path = it.path,
mimeType = it.mimeType,
type = ContentAttachmentData.Type.values()[it.mediaType]
)
}
room.sendMedias(attachments)
}
private fun handleEventDisplayed(action: RoomDetailActions.EventDisplayed) {
if (action.event.root.sendState.isSent()) { //ignore pending/local events
displayedEventsObservable.accept(action)
}
//We need to update this with the related m.replace also (to move read receipt)
action.event.annotations?.editSummary?.sourceEvents?.forEach {
room.getTimeLineEvent(it)?.let { event ->
displayedEventsObservable.accept(RoomDetailActions.EventDisplayed(event))
}
}
}
private fun handleLoadMore(action: RoomDetailActions.LoadMoreTimelineEvents) {
timeline.paginate(action.direction, PAGINATION_COUNT)
}
private fun handleRejectInvite() {
room.leave(object : MatrixCallback<Unit> {})
}
private fun handleAcceptInvite() {
room.join(callback = object : MatrixCallback<Unit> {})
}
2019-05-23 14:44:51 +00:00
private fun handleEditAction(action: RoomDetailActions.EnterEditMode) {
room.getTimeLineEvent(action.eventId)?.let {
enterEditMode(it)
}
}
private fun handleQuoteAction(action: RoomDetailActions.EnterQuoteMode) {
room.getTimeLineEvent(action.eventId)?.let {
setState {
copy(
sendMode = SendMode.QUOTE(it)
2019-05-23 14:44:51 +00:00
)
}
}
}
private fun handleReplyAction(action: RoomDetailActions.EnterReplyMode) {
room.getTimeLineEvent(action.eventId)?.let {
setState {
copy(
sendMode = SendMode.REPLY(it)
)
}
}
}
2019-07-08 17:06:17 +00:00
data class DownloadFileState(
val mimeType: String,
val file: File?,
val throwable: Throwable?
)
private fun handleDownloadFile(action: RoomDetailActions.DownloadFile) {
session.downloadFile(
FileService.DownloadMode.TO_EXPORT,
action.eventId,
2019-07-09 08:33:23 +00:00
action.messageFileContent.getFileName(),
2019-07-09 12:48:57 +00:00
action.messageFileContent.getFileUrl(),
2019-07-08 17:06:17 +00:00
action.messageFileContent.encryptedFileInfo?.toElementToDecrypt(),
object : MatrixCallback<File> {
override fun onSuccess(data: File) {
_downloadedFileEvent.postLiveEvent(DownloadFileState(
2019-07-09 08:33:23 +00:00
action.messageFileContent.getMimeType(),
2019-07-08 17:06:17 +00:00
data,
null
))
2019-07-08 17:06:17 +00:00
}
override fun onFailure(failure: Throwable) {
_downloadedFileEvent.postLiveEvent(DownloadFileState(
2019-07-09 08:33:23 +00:00
action.messageFileContent.getMimeType(),
2019-07-08 17:06:17 +00:00
null,
failure
))
2019-07-08 17:06:17 +00:00
}
})
}
2019-06-20 14:27:43 +00:00
private fun handleNavigateToEvent(action: RoomDetailActions.NavigateToEvent) {
val targetEventId = action.eventId
if (action.position != null) {
// Event is already in RAM
withState {
if (it.eventId == targetEventId) {
// ensure another click on the same permalink will also do a scroll
setState {
copy(
eventId = null
)
}
}
setState {
copy(
eventId = targetEventId
)
}
}
_navigateToEvent.postLiveEvent(targetEventId)
2019-06-20 14:27:43 +00:00
} else {
// change timeline
timeline.dispose()
timeline = room.createTimeline(targetEventId, timelineSettings)
2019-06-20 14:27:43 +00:00
timeline.start()
withState {
if (it.eventId == targetEventId) {
// ensure another click on the same permalink will also do a scroll
setState {
copy(
eventId = null
)
}
}
setState {
copy(
eventId = targetEventId,
timeline = this@RoomDetailViewModel.timeline
)
}
}
_navigateToEvent.postLiveEvent(targetEventId)
2019-06-20 14:27:43 +00:00
}
}
private fun handleResendEvent(action: RoomDetailActions.ResendMessage) {
val targetEventId = action.eventId
room.getTimeLineEvent(targetEventId)?.let {
//State must be UNDELIVERED or Failed
if (!it.root.sendState.hasFailed()) {
Timber.e("Cannot resend message, it is not failed, Cancel first")
return
}
if (it.root.isTextMessage()) {
room.resendTextMessage(it)
} else if (it.root.isImageMessage()) {
room.resendMediaMessage(it)
} else {
//TODO
}
}
}
private fun handleRemove(action: RoomDetailActions.RemoveFailedEcho) {
val targetEventId = action.eventId
room.getTimeLineEvent(targetEventId)?.let {
//State must be UNDELIVERED or Failed
if (!it.root.sendState.hasFailed()) {
Timber.e("Cannot resend message, it is not failed, Cancel first")
return
}
room.deleteFailedEcho(it)
}
}
private fun handleClearSendQueue() {
room.clearSendingQueue()
}
private fun handleResendAll() {
room.resendAllFailedMessages()
}
private fun observeEventDisplayedActions() {
// We are buffering scroll events for one second
// and keep the most recent one to set the read receipt on.
displayedEventsObservable
.buffer(1, TimeUnit.SECONDS)
.filter { it.isNotEmpty() }
.subscribeBy(onNext = { actions ->
val mostRecentEvent = actions.maxBy { it.event.displayIndex }
mostRecentEvent?.event?.root?.eventId?.let { eventId ->
2019-04-03 14:36:45 +00:00
room.setReadReceipt(eventId, callback = object : MatrixCallback<Unit> {})
}
})
.disposeOnClear()
}
private fun observeSyncState() {
session.rx()
.liveSyncState()
.subscribe { syncState ->
setState {
copy(syncState = syncState)
}
}
.disposeOnClear()
}
2018-12-29 16:54:03 +00:00
private fun observeRoomSummary() {
room.rx().liveRoomSummary()
2018-12-29 16:54:03 +00:00
.execute { async ->
copy(
asyncRoomSummary = async,
isEncrypted = room.isEncrypted()
)
2018-12-29 16:54:03 +00:00
}
}
private fun observeSummaryState() {
asyncSubscribe(RoomDetailViewState::asyncRoomSummary) { summary ->
if (summary.membership == Membership.INVITE) {
2019-07-01 10:35:48 +00:00
summary.latestEvent?.root?.senderId?.let { senderId ->
session.getUser(senderId)
}?.also {
2019-05-20 15:13:12 +00:00
setState { copy(asyncInviter = Success(it)) }
}
}
room.getStateEvent(EventType.STATE_ROOM_TOMBSTONE)?.also {
setState { copy(tombstoneEvent = it) }
}
}
}
override fun onCleared() {
timeline.dispose()
super.onCleared()
2018-12-29 16:54:03 +00:00
}
2018-12-29 16:54:03 +00:00
}