Support cancel sending and resend event with attachments
Avoid auto retry for medium and big files
This commit is contained in:
parent
8b8855d2d5
commit
a888e1e80e
|
@ -115,7 +115,7 @@ dependencies {
|
|||
def coroutines_version = "1.3.8"
|
||||
def markwon_version = '3.1.0'
|
||||
def daggerVersion = '2.25.4'
|
||||
def work_version = '2.3.3'
|
||||
def work_version = '2.4.0'
|
||||
def retrofit_version = '2.6.2'
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (c) 2020 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.matrix.android.internal.session.room.send
|
||||
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* We cannot use work manager cancellation mechanism because cancelling a work will just ignore
|
||||
* any follow up send that was already queued.
|
||||
* We use this class to track cancel requests, the workers will look for this to check for cancellation request
|
||||
* and just ignore the work request and continue by returning success.
|
||||
*
|
||||
* Known limitation, for now requests are not persisted
|
||||
*/
|
||||
@SessionScope
|
||||
class CancelSendTracker @Inject constructor() {
|
||||
|
||||
data class Request(
|
||||
val localId: String,
|
||||
val roomId: String
|
||||
)
|
||||
|
||||
private val cancellingRequests = ArrayList<Request>()
|
||||
|
||||
fun markLocalEchoForCancel(eventId: String, roomId: String) {
|
||||
synchronized(cancellingRequests) {
|
||||
cancellingRequests.add(Request(eventId, roomId))
|
||||
}
|
||||
}
|
||||
|
||||
fun isCancelRequestedFor(eventId: String?, roomId: String?): Boolean {
|
||||
val index = synchronized(cancellingRequests) {
|
||||
cancellingRequests.indexOfFirst { it.localId == eventId && it.roomId == roomId }
|
||||
}
|
||||
return index != -1
|
||||
}
|
||||
|
||||
fun markCancelled(eventId: String, roomId: String) {
|
||||
synchronized(cancellingRequests) {
|
||||
val index = cancellingRequests.indexOfFirst { it.localId == eventId && it.roomId == roomId }
|
||||
if (index != -1) {
|
||||
cancellingRequests.removeAt(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -55,6 +55,7 @@ sealed class RoomDetailAction : VectorViewModelAction {
|
|||
|
||||
data class ResendMessage(val eventId: String) : RoomDetailAction()
|
||||
data class RemoveFailedEcho(val eventId: String) : RoomDetailAction()
|
||||
data class CancelSend(val eventId: String) : RoomDetailAction()
|
||||
|
||||
data class ReplyToOptions(val eventId: String, val optionIndex: Int, val optionValue: String) : RoomDetailAction()
|
||||
|
||||
|
|
|
@ -1617,6 +1617,9 @@ class RoomDetailFragment @Inject constructor(
|
|||
is EventSharedAction.Remove -> {
|
||||
roomDetailViewModel.handle(RoomDetailAction.RemoveFailedEcho(action.eventId))
|
||||
}
|
||||
is EventSharedAction.Cancel -> {
|
||||
roomDetailViewModel.handle(RoomDetailAction.CancelSend(action.eventId))
|
||||
}
|
||||
is EventSharedAction.ReportContentSpam -> {
|
||||
roomDetailViewModel.handle(RoomDetailAction.ReportContent(
|
||||
action.eventId, action.senderId, "This message is spam", spam = true))
|
||||
|
|
|
@ -19,6 +19,7 @@ package im.vector.app.features.home.room.detail
|
|||
import android.net.Uri
|
||||
import androidx.annotation.IdRes
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.work.WorkManager
|
||||
import com.airbnb.mvrx.FragmentViewModelContext
|
||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||
import com.airbnb.mvrx.Success
|
||||
|
@ -282,6 +283,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||
is RoomDetailAction.AddJitsiWidget -> handleAddJitsiConference(action)
|
||||
is RoomDetailAction.RemoveWidget -> handleDeleteWidget(action.widgetId)
|
||||
is RoomDetailAction.EnsureNativeWidgetAllowed -> handleCheckWidgetAllowed(action)
|
||||
is RoomDetailAction.CancelSend -> handleCancel(action)
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
|
@ -1051,9 +1053,9 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||
return
|
||||
}
|
||||
when {
|
||||
it.root.isTextMessage() -> room.resendTextMessage(it)
|
||||
it.root.isImageMessage() -> room.resendMediaMessage(it)
|
||||
else -> {
|
||||
it.root.isTextMessage() -> room.resendTextMessage(it)
|
||||
it.root.isAttachmentMessage() -> room.resendMediaMessage(it)
|
||||
else -> {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
@ -1072,6 +1074,18 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleCancel(action: RoomDetailAction.CancelSend) {
|
||||
val targetEventId = action.eventId
|
||||
room.getTimeLineEvent(targetEventId)?.let {
|
||||
// State must be UNDELIVERED or Failed
|
||||
if (!it.root.sendState.isSending()) {
|
||||
Timber.e("Cannot resend message, it is not failed, Cancel first")
|
||||
return
|
||||
}
|
||||
room.cancelSend(action.eventId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleClearSendQueue() {
|
||||
room.clearSendingQueue()
|
||||
}
|
||||
|
|
|
@ -230,6 +230,14 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
|||
add(EventSharedAction.Resend(eventId))
|
||||
}
|
||||
add(EventSharedAction.Remove(eventId))
|
||||
if (vectorPreferences.developerMode()) {
|
||||
add(EventSharedAction.ViewSource(timelineEvent.root.toContentStringWithIndent()))
|
||||
if (timelineEvent.isEncrypted() && timelineEvent.root.mxDecryptionResult != null) {
|
||||
val decryptedContent = timelineEvent.root.toClearContentStringWithIndent()
|
||||
?: stringProvider.getString(R.string.encryption_information_decryption_error)
|
||||
add(EventSharedAction.ViewDecryptedSource(decryptedContent))
|
||||
}
|
||||
}
|
||||
} else if (timelineEvent.root.sendState.isSending()) {
|
||||
// TODO is uploading attachment?
|
||||
if (canCancel(timelineEvent)) {
|
||||
|
@ -321,7 +329,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
|||
}
|
||||
|
||||
private fun canCancel(@Suppress("UNUSED_PARAMETER") event: TimelineEvent): Boolean {
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
private fun canReply(event: TimelineEvent, messageContent: MessageContent?, actionPermissions: ActionPermissions): Boolean {
|
||||
|
@ -365,7 +373,9 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
|||
}
|
||||
|
||||
private fun canRetry(event: TimelineEvent, actionPermissions: ActionPermissions): Boolean {
|
||||
return event.root.sendState.hasFailed() && event.root.isTextMessage() && actionPermissions.canSendMessage
|
||||
return event.root.sendState.hasFailed()
|
||||
&& actionPermissions.canSendMessage
|
||||
&& (event.root.isAttachmentMessage() || event.root.isTextMessage())
|
||||
}
|
||||
|
||||
private fun canViewReactions(event: TimelineEvent): Boolean {
|
||||
|
|
Loading…
Reference in New Issue