Retry send and cancel send for failed to send messages (#1088)

* is now possible to open the confirmation dialog and pass the transaction id

* task completed added some tests

* changelog

* title visibility set to .visible
This commit is contained in:
Mauro
2023-06-16 11:00:54 +02:00
committed by GitHub
parent 163e38b1a9
commit ac41704821
17 changed files with 281 additions and 23 deletions
@@ -70,6 +70,9 @@ enum RoomScreenViewAction {
case handlePasteOrDrop(provider: NSItemProvider)
case tappedOnUser(userID: String)
case retrySend(transactionID: String?)
case cancelSend(transactionID: String?)
}
struct RoomScreenViewState: BindableState {
@@ -109,6 +112,8 @@ struct RoomScreenViewStateBindings {
var debugInfo: TimelineItemDebugInfo?
var actionMenuInfo: TimelineItemActionMenuInfo?
var sendFailedConfirmationDialogInfo: SendFailedConfirmationDialogInfo?
}
struct TimelineItemActionMenuInfo: Identifiable {
@@ -119,6 +124,12 @@ struct TimelineItemActionMenuInfo: Identifiable {
}
}
struct SendFailedConfirmationDialogInfo: ConfirmationDialogProtocol {
let title = L10n.screenRoomRetrySendMenuTitle
let transactionID: String?
}
enum RoomScreenErrorType: Hashable {
/// A specific error message shown in an alert.
case alert(String)
@@ -105,10 +105,13 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
handlePasteOrDrop(provider)
case .tappedOnUser(userID: let userID):
Task { await handleTappedUser(userID: userID) }
case .displayEmojiPicker(let itemID):
guard let item = state.items.first(where: { $0.id == itemID }), item.isReactable else { return }
callback?(.displayEmojiPicker(itemID: itemID))
case .retrySend(let transactionID):
Task { await handleRetrySend(transactionID: transactionID) }
case .cancelSend(let transactionID):
Task { await handleCancelSend(transactionID: transactionID) }
}
}
@@ -470,6 +473,22 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
}
}
private func handleRetrySend(transactionID: String?) async {
guard let transactionID else {
return
}
await roomProxy.retrySend(transactionID: transactionID)
}
private func handleCancelSend(transactionID: String?) async {
guard let transactionID else {
return
}
await roomProxy.cancelSend(transactionID: transactionID)
}
private static let loadingIndicatorIdentifier = "RoomScreenLoadingIndicator"
private func showLoadingIndicator(with delay: Duration) -> Task<Void, Never> {
@@ -62,6 +62,14 @@ struct RoomScreen: View {
context.send(viewAction: .handlePasteOrDrop(provider: provider))
return true
}
.confirmationDialog(item: $context.sendFailedConfirmationDialogInfo, titleVisibility: .visible) { item in
Button(L10n.screenRoomRetrySendMenuSendAgainAction) {
context.send(viewAction: .retrySend(transactionID: item.transactionID))
}
Button(L10n.screenRoomRetrySendMenuRemoveAction, role: .destructive) {
context.send(viewAction: .cancelSend(transactionID: item.transactionID))
}
}
}
private var timeline: some View {
@@ -146,30 +146,47 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
@ViewBuilder
var contentWithTimestamp: some View {
if isTextItem {
if isTextItem || isMediaType {
ZStack(alignment: .bottomTrailing) {
contentWithReply
localizedSendInfo
}
} else if isMediaType {
ZStack(alignment: .bottomTrailing) {
contentWithReply
localizedSendInfo
.padding(.horizontal, 4)
.padding(.vertical, 2)
.background(Color.element.system)
.cornerRadius(10)
.padding(.trailing, 4)
.padding(.bottom, 4)
interactiveLocalizedSendInfo
}
} else {
HStack(alignment: .bottom, spacing: 4) {
contentWithReply
localizedSendInfo
interactiveLocalizedSendInfo
}
}
}
@ViewBuilder
var interactiveLocalizedSendInfo: some View {
if timelineItem.properties.deliveryStatus == .sendingFailed {
backgroundedLocalizedSendInfo
.onTapGesture {
context.sendFailedConfirmationDialogInfo = .init(transactionID: timelineItem.properties.transactionID)
}
} else {
backgroundedLocalizedSendInfo
}
}
@ViewBuilder
var backgroundedLocalizedSendInfo: some View {
if isMediaType {
localizedSendInfo
.padding(.horizontal, 4)
.padding(.vertical, 2)
.background(Color.element.system)
.cornerRadius(10)
.padding(.trailing, 4)
.padding(.bottom, 4)
} else {
localizedSendInfo
}
}
@ViewBuilder
var localizedSendInfo: some View {
HStack(spacing: 4) {
@@ -69,6 +69,9 @@ struct TimelineItemStatusView: View {
.resizable()
.foregroundColor(.element.alert)
.frame(width: 16, height: 16)
.onTapGesture {
context.sendFailedConfirmationDialogInfo = .init(transactionID: timelineItem.properties.transactionID)
}
}
// The bubbles handle the failure internally
}