diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt
index 152a018e78..6ae42de90c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt
@@ -132,4 +132,9 @@ interface SendService {
* Resend all failed messages one by one (and keep order)
*/
fun resendAllFailedMessages()
+
+ /**
+ * Cancel all failed messages
+ */
+ fun cancelAllFailedMessages()
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt
index a12962b51f..c5b8b42b3c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt
@@ -232,6 +232,14 @@ internal class DefaultSendService @AssistedInject constructor(
}
}
+ override fun cancelAllFailedMessages() {
+ taskExecutor.executorScope.launch {
+ localEchoRepository.getAllFailedEventsToResend(roomId).forEach { event ->
+ cancelSend(event.eventId)
+ }
+ }
+ }
+
override fun sendMedia(attachment: ContentAttachmentData,
compressBeforeSending: Boolean,
roomIds: Set): Cancelable {
diff --git a/vector/src/main/java/im/vector/app/core/ui/views/FailedMessagesWarningView.kt b/vector/src/main/java/im/vector/app/core/ui/views/FailedMessagesWarningView.kt
new file mode 100644
index 0000000000..f9518552a3
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/core/ui/views/FailedMessagesWarningView.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.core.ui.views
+
+import android.content.Context
+import android.util.AttributeSet
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.core.view.isVisible
+import im.vector.app.R
+import im.vector.app.databinding.ViewFailedMessagesWarningBinding
+
+class FailedMessagesWarningView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0
+) : ConstraintLayout(context, attrs, defStyleAttr) {
+
+ interface Callback {
+ fun onDeleteAllClicked()
+ fun onRetryClicked()
+ }
+
+ var callback: Callback? = null
+
+ private lateinit var views: ViewFailedMessagesWarningBinding
+
+ init {
+ setupViews()
+ }
+
+ private fun setupViews() {
+ inflate(context, R.layout.view_failed_messages_warning, this)
+ views = ViewFailedMessagesWarningBinding.bind(this)
+
+ views.failedMessagesDeleteAllButton.setOnClickListener { callback?.onDeleteAllClicked() }
+ views.failedMessagesRetryButton.setOnClickListener { callback?.onRetryClicked() }
+ }
+
+ fun render(hasFailedMessages: Boolean) {
+ isVisible = hasFailedMessages
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt
index efc495a379..ecbd0b0d30 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt
@@ -106,4 +106,7 @@ sealed class RoomDetailAction : VectorViewModelAction {
data class DoNotShowPreviewUrlFor(val eventId: String, val url: String) : RoomDetailAction()
data class ComposerFocusChange(val focused: Boolean) : RoomDetailAction()
+
+ // Failed messages
+ object RemoveAllFailedMessages : RoomDetailAction()
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt
index 8087a0072f..1df583187e 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt
@@ -94,6 +94,7 @@ import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.ui.views.CurrentCallsView
import im.vector.app.core.ui.views.KnownCallsViewHolder
import im.vector.app.core.ui.views.ActiveConferenceView
+import im.vector.app.core.ui.views.FailedMessagesWarningView
import im.vector.app.core.ui.views.JumpToReadMarkerView
import im.vector.app.core.ui.views.NotificationAreaView
import im.vector.app.core.utils.Debouncer
@@ -157,7 +158,6 @@ import im.vector.app.features.permalink.NavigationInterceptor
import im.vector.app.features.permalink.PermalinkHandler
import im.vector.app.features.reactions.EmojiReactionPickerActivity
import im.vector.app.features.roomprofile.RoomProfileActivity
-import im.vector.app.features.roomprofile.alias.RoomAliasAction
import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.settings.VectorSettingsActivity
import im.vector.app.features.share.SharedData
@@ -326,6 +326,7 @@ class RoomDetailFragment @Inject constructor(
setupJumpToBottomView()
setupConfBannerView()
setupEmojiPopup()
+ setupFailedMessagesWarningView()
views.roomToolbarContentView.debouncedClicks {
navigator.openRoomProfile(requireActivity(), roomDetailArgs.roomId)
@@ -558,6 +559,25 @@ class RoomDetailFragment @Inject constructor(
}
}
+ private fun setupFailedMessagesWarningView() {
+ views.failedMessagesWarningView.callback = object : FailedMessagesWarningView.Callback {
+ override fun onDeleteAllClicked() {
+ AlertDialog.Builder(requireContext())
+ .setTitle(R.string.event_status_delete_all_failed_dialog_title)
+ .setMessage(getString(R.string.event_status_delete_all_failed_dialog_message))
+ .setNegativeButton(R.string.no, null)
+ .setPositiveButton(R.string.yes) { _, _ ->
+ roomDetailViewModel.handle(RoomDetailAction.RemoveAllFailedMessages)
+ }
+ .show()
+ }
+
+ override fun onRetryClicked() {
+ roomDetailViewModel.handle(RoomDetailAction.ResendAll)
+ }
+ }
+ }
+
private fun joinJitsiRoom(jitsiWidget: Widget, enableVideo: Boolean) {
navigator.openRoomWidget(requireContext(), roomDetailArgs.roomId, jitsiWidget, mapOf(JitsiCallViewModel.ENABLE_VIDEO_OPTION to enableVideo))
}
@@ -777,10 +797,6 @@ class RoomDetailFragment @Inject constructor(
navigator.openRoomProfile(requireActivity(), roomDetailArgs.roomId)
true
}
- R.id.resend_all -> {
- roomDetailViewModel.handle(RoomDetailAction.ResendAll)
- true
- }
R.id.open_matrix_apps -> {
roomDetailViewModel.handle(RoomDetailAction.ManageIntegrations)
true
@@ -1172,6 +1188,7 @@ class RoomDetailFragment @Inject constructor(
val summary = state.asyncRoomSummary()
renderToolbar(summary, state.typingMessage)
views.activeConferenceView.render(state)
+ views.failedMessagesWarningView.render(state.hasFailedSending)
val inviter = state.asyncInviter()
if (summary?.membership == Membership.JOIN) {
views.jumpToBottomView.count = summary.notificationCount
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt
index f293eadf50..cf6862a28d 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt
@@ -322,6 +322,8 @@ class RoomDetailViewModel @AssistedInject constructor(
)
}
is RoomDetailAction.DoNotShowPreviewUrlFor -> handleDoNotShowPreviewUrlFor(action)
+ RoomDetailAction.RemoveAllFailedMessages -> handleRemoveAllFailedMessages()
+ RoomDetailAction.ResendAll -> handleResendAll()
}.exhaustive
}
@@ -660,10 +662,8 @@ class RoomDetailViewModel @AssistedInject constructor(
return@withState false
}
when (itemId) {
- R.id.resend_all -> state.asyncRoomSummary()?.hasFailedSending == true
R.id.timeline_setting -> true
R.id.invite -> state.canInvite
- R.id.clear_all -> state.asyncRoomSummary()?.hasFailedSending == true
R.id.open_matrix_apps -> true
R.id.voice_call,
R.id.video_call -> callManager.getCallsByRoomId(state.roomId).isEmpty()
@@ -1223,6 +1223,10 @@ class RoomDetailViewModel @AssistedInject constructor(
room.resendAllFailedMessages()
}
+ private fun handleRemoveAllFailedMessages() {
+ room.cancelAllFailedMessages()
+ }
+
private fun observeEventDisplayedActions() {
// We are buffering scroll events for one second
// and keep the most recent one to set the read receipt on.
@@ -1437,7 +1441,7 @@ class RoomDetailViewModel @AssistedInject constructor(
roomSummariesHolder.set(summary)
setState {
val typingMessage = typingHelper.getTypingMessage(summary.typingUsers)
- copy(typingMessage = typingMessage)
+ copy(typingMessage = typingMessage, hasFailedSending = summary.hasFailedSending)
}
if (summary.membership == Membership.INVITE) {
summary.inviterId?.let { inviterId ->
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt
index 8c2b3ffe98..965733c424 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt
@@ -75,7 +75,8 @@ data class RoomDetailViewState(
val canInvite: Boolean = true,
val isAllowedToManageWidgets: Boolean = false,
val isAllowedToStartWebRTCCall: Boolean = true,
- val showDialerOption: Boolean = false
+ val showDialerOption: Boolean = false,
+ val hasFailedSending: Boolean = false
) : MvRxState {
constructor(args: RoomDetailArgs) : this(
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageFileItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageFileItem.kt
index 9dc777676a..b215fa5dd5 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageFileItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageFileItem.kt
@@ -28,7 +28,6 @@ import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
import im.vector.app.features.home.room.detail.timeline.helper.ContentDownloadStateTrackerBinder
import im.vector.app.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder
-import org.matrix.android.sdk.api.session.room.send.SendState
@EpoxyModelClass(layout = R.layout.item_timeline_event_base)
abstract class MessageFileItem : AbsMessageItem() {
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageImageVideoItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageImageVideoItem.kt
index e0085105d2..2c0d1fcfbd 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageImageVideoItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageImageVideoItem.kt
@@ -19,7 +19,6 @@ package im.vector.app.features.home.room.detail.timeline.item
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
-import android.widget.ProgressBar
import androidx.core.view.ViewCompat
import androidx.core.view.isVisible
import com.airbnb.epoxy.EpoxyAttribute
@@ -29,7 +28,6 @@ import im.vector.app.core.files.LocalFilesHelper
import im.vector.app.core.glide.GlideApp
import im.vector.app.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder
import im.vector.app.features.media.ImageContentRenderer
-import org.matrix.android.sdk.api.session.room.send.SendState
@EpoxyModelClass(layout = R.layout.item_timeline_event_base)
abstract class MessageImageVideoItem : AbsMessageItem() {
diff --git a/vector/src/main/res/layout/fragment_room_detail.xml b/vector/src/main/res/layout/fragment_room_detail.xml
index df36d7e225..a6909a2893 100644
--- a/vector/src/main/res/layout/fragment_room_detail.xml
+++ b/vector/src/main/res/layout/fragment_room_detail.xml
@@ -86,7 +86,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="top"
- app:constraint_referenced_ids="composerLayout,notificationAreaView" />
+ app:constraint_referenced_ids="composerLayout,notificationAreaView, failedMessagesWarningView" />
+
+
+ app:constraint_referenced_ids="composerLayout,notificationAreaView, failedMessagesWarningView" />
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/vector/src/main/res/menu/menu_timeline.xml b/vector/src/main/res/menu/menu_timeline.xml
index 1f4e2736b1..e3208c4cc4 100644
--- a/vector/src/main/res/menu/menu_timeline.xml
+++ b/vector/src/main/res/menu/menu_timeline.xml
@@ -52,22 +52,6 @@
app:actionLayout="@layout/custom_action_item_layout_badge"
app:showAsAction="ifRoom" />
-
-
-
-
- Edit
Reply
- "Retry"
+ Retry
"Join a room to start using the app."
"Sent you an invitation"
Invited by %s
@@ -3243,5 +3243,9 @@
Sending
Sent
Failed
+ Delete all failed messages
Do you want to cancel sending message?
+ Messages failed to send
+ Delete unsent messages
+ Are you sure you want to delete all unsent messages in this room?